summaryrefslogtreecommitdiff
path: root/meshmc/launcher/minecraft/launch
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:45:07 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:45:07 +0300
commit31b9a8949ed0a288143e23bf739f2eb64fdc63be (patch)
tree8a984fa143c38fccad461a77792d6864f3e82cd3 /meshmc/launcher/minecraft/launch
parent934382c8a1ce738589dee9ee0f14e1cec812770e (diff)
parentfad6a1066616b69d7f5fef01178efdf014c59537 (diff)
downloadProject-Tick-31b9a8949ed0a288143e23bf739f2eb64fdc63be.tar.gz
Project-Tick-31b9a8949ed0a288143e23bf739f2eb64fdc63be.zip
Add 'meshmc/' from commit 'fad6a1066616b69d7f5fef01178efdf014c59537'
git-subtree-dir: meshmc git-subtree-mainline: 934382c8a1ce738589dee9ee0f14e1cec812770e git-subtree-split: fad6a1066616b69d7f5fef01178efdf014c59537
Diffstat (limited to 'meshmc/launcher/minecraft/launch')
-rw-r--r--meshmc/launcher/minecraft/launch/ClaimAccount.cpp49
-rw-r--r--meshmc/launcher/minecraft/launch/ClaimAccount.h61
-rw-r--r--meshmc/launcher/minecraft/launch/CreateGameFolders.cpp50
-rw-r--r--meshmc/launcher/minecraft/launch/CreateGameFolders.h58
-rw-r--r--meshmc/launcher/minecraft/launch/DirectJavaLaunch.cpp173
-rw-r--r--meshmc/launcher/minecraft/launch/DirectJavaLaunch.h80
-rw-r--r--meshmc/launcher/minecraft/launch/ExtractNatives.cpp133
-rw-r--r--meshmc/launcher/minecraft/launch/ExtractNatives.h59
-rw-r--r--meshmc/launcher/minecraft/launch/MeshMCPartLaunch.cpp237
-rw-r--r--meshmc/launcher/minecraft/launch/MeshMCPartLaunch.h83
-rw-r--r--meshmc/launcher/minecraft/launch/MinecraftServerTarget.cpp85
-rw-r--r--meshmc/launcher/minecraft/launch/MinecraftServerTarget.h52
-rw-r--r--meshmc/launcher/minecraft/launch/ModMinecraftJar.cpp103
-rw-r--r--meshmc/launcher/minecraft/launch/ModMinecraftJar.h60
-rw-r--r--meshmc/launcher/minecraft/launch/PrintInstanceInfo.cpp166
-rw-r--r--meshmc/launcher/minecraft/launch/PrintInstanceInfo.h66
-rw-r--r--meshmc/launcher/minecraft/launch/ReconstructAssets.cpp61
-rw-r--r--meshmc/launcher/minecraft/launch/ReconstructAssets.h56
-rw-r--r--meshmc/launcher/minecraft/launch/ScanModFolders.cpp85
-rw-r--r--meshmc/launcher/minecraft/launch/ScanModFolders.h66
-rw-r--r--meshmc/launcher/minecraft/launch/VerifyJavaInstall.cpp374
-rw-r--r--meshmc/launcher/minecraft/launch/VerifyJavaInstall.h61
22 files changed, 2218 insertions, 0 deletions
diff --git a/meshmc/launcher/minecraft/launch/ClaimAccount.cpp b/meshmc/launcher/minecraft/launch/ClaimAccount.cpp
new file mode 100644
index 0000000000..a1ed6fdb69
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ClaimAccount.cpp
@@ -0,0 +1,49 @@
+/* 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 "ClaimAccount.h"
+#include <launch/LaunchTask.h>
+
+#include "Application.h"
+#include "minecraft/auth/AccountList.h"
+
+ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session)
+ : LaunchStep(parent)
+{
+ if (session->status == AuthSession::Status::PlayableOnline &&
+ !session->demo) {
+ auto accounts = APPLICATION->accounts();
+ m_account = accounts->getAccountByProfileName(session->player_name);
+ }
+}
+
+void ClaimAccount::executeTask()
+{
+ if (m_account) {
+ lock.reset(new UseLock(m_account));
+ emitSucceeded();
+ }
+}
+
+void ClaimAccount::finalize()
+{
+ lock.reset();
+}
diff --git a/meshmc/launcher/minecraft/launch/ClaimAccount.h b/meshmc/launcher/minecraft/launch/ClaimAccount.h
new file mode 100644
index 0000000000..d3f64bd8ed
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ClaimAccount.h
@@ -0,0 +1,61 @@
+/* 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 <minecraft/auth/MinecraftAccount.h>
+
+class ClaimAccount : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit ClaimAccount(LaunchTask* parent, AuthSessionPtr session);
+ virtual ~ClaimAccount() {};
+
+ void executeTask() override;
+ void finalize() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+
+ private:
+ std::unique_ptr<UseLock> lock;
+ MinecraftAccountPtr m_account;
+};
diff --git a/meshmc/launcher/minecraft/launch/CreateGameFolders.cpp b/meshmc/launcher/minecraft/launch/CreateGameFolders.cpp
new file mode 100644
index 0000000000..32cd010905
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/CreateGameFolders.cpp
@@ -0,0 +1,50 @@
+/* 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 "CreateGameFolders.h"
+#include "minecraft/MinecraftInstance.h"
+#include "launch/LaunchTask.h"
+#include "FileSystem.h"
+
+CreateGameFolders::CreateGameFolders(LaunchTask* parent) : LaunchStep(parent) {}
+
+void CreateGameFolders::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance =
+ std::dynamic_pointer_cast<MinecraftInstance>(instance);
+
+ if (!FS::ensureFolderPathExists(minecraftInstance->gameRoot())) {
+ emit logLine("Couldn't create the main game folder",
+ MessageLevel::Error);
+ emitFailed(tr("Couldn't create the main game folder"));
+ return;
+ }
+
+ // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder
+ // is created.
+ if (!FS::ensureFolderPathExists(FS::PathCombine(
+ minecraftInstance->gameRoot(), "server-resource-packs"))) {
+ emit logLine("Couldn't create the 'server-resource-packs' folder",
+ MessageLevel::Error);
+ }
+ emitSucceeded();
+}
diff --git a/meshmc/launcher/minecraft/launch/CreateGameFolders.h b/meshmc/launcher/minecraft/launch/CreateGameFolders.h
new file mode 100644
index 0000000000..76a9225c21
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/CreateGameFolders.h
@@ -0,0 +1,58 @@
+/* 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 <minecraft/auth/AuthSession.h>
+
+// Create the main .minecraft for the instance and any other necessary folders
+class CreateGameFolders : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit CreateGameFolders(LaunchTask* parent);
+ virtual ~CreateGameFolders() {};
+
+ virtual void executeTask();
+ virtual bool canAbort() const
+ {
+ return false;
+ }
+};
diff --git a/meshmc/launcher/minecraft/launch/DirectJavaLaunch.cpp b/meshmc/launcher/minecraft/launch/DirectJavaLaunch.cpp
new file mode 100644
index 0000000000..c1dc249eac
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/DirectJavaLaunch.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 "DirectJavaLaunch.h"
+#include <launch/LaunchTask.h>
+#include <minecraft/MinecraftInstance.h>
+#include <FileSystem.h>
+#include <Commandline.h>
+#include <QStandardPaths>
+
+DirectJavaLaunch::DirectJavaLaunch(LaunchTask* parent) : LaunchStep(parent)
+{
+ connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this,
+ &DirectJavaLaunch::on_state);
+}
+
+void DirectJavaLaunch::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance =
+ std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ QStringList args = minecraftInstance->javaArguments();
+
+ args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
+
+ auto classPathEntries = minecraftInstance->getClassPath();
+ args.append("-cp");
+ QString classpath;
+#ifdef Q_OS_WIN32
+ classpath = classPathEntries.join(';');
+#else
+ classpath = classPathEntries.join(':');
+#endif
+ args.append(classpath);
+ args.append(minecraftInstance->getMainClass());
+
+ QString allArgs = args.join(", ");
+ emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) +
+ "]\n\n",
+ MessageLevel::MeshMC);
+
+ auto javaPath =
+ FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
+
+ m_process.setProcessEnvironment(instance->createEnvironment());
+
+ // make detachable - this will keep the process running even if the object
+ // is destroyed
+ m_process.setDetachable(true);
+
+ auto mcArgs =
+ minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin);
+ args.append(mcArgs);
+
+ QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
+ if (!wrapperCommandStr.isEmpty()) {
+ auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
+ auto wrapperCommand = wrapperArgs.takeFirst();
+ auto realWrapperCommand =
+ QStandardPaths::findExecutable(wrapperCommand);
+ if (realWrapperCommand.isEmpty()) {
+ const char* reason =
+ QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
+ emit logLine(QString(reason).arg(wrapperCommand),
+ MessageLevel::Fatal);
+ emitFailed(tr(reason).arg(wrapperCommand));
+ return;
+ }
+ emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n",
+ MessageLevel::MeshMC);
+ args.prepend(javaPath);
+ m_process.start(wrapperCommand, wrapperArgs + args);
+ } else {
+ m_process.start(javaPath, args);
+ }
+}
+
+void DirectJavaLaunch::on_state(LoggedProcess::State state)
+{
+ switch (state) {
+ case LoggedProcess::FailedToStart: {
+ //: Error message displayed if instance can't start
+ const char* reason = QT_TR_NOOP("Could not launch minecraft!");
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(tr(reason));
+ return;
+ }
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed: {
+ m_parent->setPid(-1);
+ emitFailed(tr("Game crashed."));
+ return;
+ }
+ case LoggedProcess::Finished: {
+ m_parent->setPid(-1);
+ // if the exit code wasn't 0, report this as a crash
+ auto exitCode = m_process.exitCode();
+ if (exitCode != 0) {
+ emitFailed(tr("Game crashed."));
+ return;
+ }
+ // FIXME: make this work again
+ // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE",
+ // QString(exitCode)); run post-exit
+ emitSucceeded();
+ break;
+ }
+ case LoggedProcess::Running:
+ emit logLine(QString("Minecraft process ID: %1\n\n")
+ .arg(m_process.processId()),
+ MessageLevel::MeshMC);
+ m_parent->setPid(m_process.processId());
+ m_parent->instance()->setLastLaunch();
+ break;
+ default:
+ break;
+ }
+}
+
+void DirectJavaLaunch::setWorkingDirectory(const QString& wd)
+{
+ m_process.setWorkingDirectory(wd);
+}
+
+void DirectJavaLaunch::proceed()
+{
+ // nil
+}
+
+bool DirectJavaLaunch::abort()
+{
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running || state == LoggedProcess::Starting) {
+ m_process.kill();
+ }
+ return true;
+}
diff --git a/meshmc/launcher/minecraft/launch/DirectJavaLaunch.h b/meshmc/launcher/minecraft/launch/DirectJavaLaunch.h
new file mode 100644
index 0000000000..f696e999ea
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/DirectJavaLaunch.h
@@ -0,0 +1,80 @@
+/* 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 <minecraft/auth/AuthSession.h>
+
+#include "MinecraftServerTarget.h"
+
+class DirectJavaLaunch : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit DirectJavaLaunch(LaunchTask* parent);
+ virtual ~DirectJavaLaunch() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual void proceed();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString& wd);
+ void setAuthSession(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
+
+ void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
+ {
+ m_serverToJoin = std::move(serverToJoin);
+ }
+
+ private slots:
+ void on_state(LoggedProcess::State state);
+
+ private:
+ LoggedProcess m_process;
+ QString m_command;
+ AuthSessionPtr m_session;
+ MinecraftServerTargetPtr m_serverToJoin;
+};
diff --git a/meshmc/launcher/minecraft/launch/ExtractNatives.cpp b/meshmc/launcher/minecraft/launch/ExtractNatives.cpp
new file mode 100644
index 0000000000..706820468b
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ExtractNatives.cpp
@@ -0,0 +1,133 @@
+/* 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 "ExtractNatives.h"
+#include <minecraft/MinecraftInstance.h>
+#include <launch/LaunchTask.h>
+
+#include "MMCZip.h"
+#include "FileSystem.h"
+#include <QDir>
+
+#ifdef major
+#undef major
+#endif
+#ifdef minor
+#undef minor
+#endif
+
+static QString replaceSuffix(QString target, const QString& suffix,
+ const QString& replacement)
+{
+ if (!target.endsWith(suffix)) {
+ return target;
+ }
+ target.resize(target.length() - suffix.length());
+ return target + replacement;
+}
+
+static bool unzipNatives(QString source, QString targetFolder,
+ bool applyJnilibHack, bool nativeOpenAL,
+ bool nativeGLFW)
+{
+ QDir directory(targetFolder);
+ QStringList entries = MMCZip::listEntries(source);
+ if (entries.isEmpty()) {
+ return false;
+ }
+ for (const auto& name : entries) {
+ auto lowercase = name.toLower();
+ if (nativeGLFW && name.contains("glfw")) {
+ continue;
+ }
+ if (nativeOpenAL && name.contains("openal")) {
+ continue;
+ }
+ // Skip directories
+ if (name.endsWith('/'))
+ continue;
+
+ QString outName = name;
+ if (applyJnilibHack) {
+ outName = replaceSuffix(outName, ".jnilib", ".dylib");
+ }
+ QString absFilePath = directory.absoluteFilePath(outName);
+ if (!MMCZip::extractRelFile(source, name, absFilePath)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ExtractNatives::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance =
+ std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ auto toExtract = minecraftInstance->getNativeJars();
+ if (toExtract.isEmpty()) {
+ emitSucceeded();
+ return;
+ }
+ auto settings = minecraftInstance->settings();
+ bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
+ bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
+
+ auto outputPath = minecraftInstance->getNativePath();
+ auto javaVersion = minecraftInstance->getJavaVersion();
+ bool jniHackEnabled = javaVersion.major() >= 8;
+ for (const auto& source : toExtract) {
+ if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL,
+ nativeGLFW)) {
+ const char* reason = QT_TR_NOOP(
+ "Couldn't extract native jar '%1' to destination '%2'");
+ emit logLine(QString(reason).arg(source, outputPath),
+ MessageLevel::Fatal);
+ emitFailed(tr(reason).arg(source, outputPath));
+ }
+ }
+ emitSucceeded();
+}
+
+void ExtractNatives::finalize()
+{
+ auto instance = m_parent->instance();
+ QString target_dir = FS::PathCombine(instance->instanceRoot(), "natives/");
+ QDir dir(target_dir);
+ dir.removeRecursively();
+}
diff --git a/meshmc/launcher/minecraft/launch/ExtractNatives.h b/meshmc/launcher/minecraft/launch/ExtractNatives.h
new file mode 100644
index 0000000000..0550603f7e
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ExtractNatives.h
@@ -0,0 +1,59 @@
+/* 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 <memory>
+#include "minecraft/auth/AuthSession.h"
+
+// FIXME: temporary wrapper for existing task.
+class ExtractNatives : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit ExtractNatives(LaunchTask* parent) : LaunchStep(parent) {};
+ virtual ~ExtractNatives() {};
+
+ void executeTask() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+ void finalize() override;
+};
diff --git a/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.cpp b/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.cpp
new file mode 100644
index 0000000000..83f8c4436d
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.cpp
@@ -0,0 +1,237 @@
+/* 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 "MeshMCPartLaunch.h"
+
+#include <QStandardPaths>
+
+#include "launch/LaunchTask.h"
+#include "minecraft/MinecraftInstance.h"
+#include "FileSystem.h"
+#include "Commandline.h"
+#include "Application.h"
+
+MeshMCPartLaunch::MeshMCPartLaunch(LaunchTask* parent) : LaunchStep(parent)
+{
+ connect(&m_process, &LoggedProcess::log, this, &MeshMCPartLaunch::logLines);
+ connect(&m_process, &LoggedProcess::stateChanged, this,
+ &MeshMCPartLaunch::on_state);
+}
+
+#ifdef Q_OS_WIN
+// returns 8.3 file format from long path
+#include <windows.h>
+QString shortPathName(const QString& file)
+{
+ auto input = file.toStdWString();
+ std::wstring output;
+ long length = GetShortPathNameW(input.c_str(), NULL, 0);
+ // NOTE: this resizing might seem weird...
+ // when GetShortPathNameW fails, it returns length including null character
+ // when it succeeds, it returns length excluding null character
+ // See:
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
+ output.resize(length);
+ GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length);
+ output.resize(length - 1);
+ QString ret = QString::fromStdWString(output);
+ return ret;
+}
+#endif
+
+// if the string survives roundtrip through local 8bit encoding...
+bool fitsInLocal8bit(const QString& string)
+{
+ return string == QString::fromLocal8Bit(string.toLocal8Bit());
+}
+
+void MeshMCPartLaunch::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance =
+ std::dynamic_pointer_cast<MinecraftInstance>(instance);
+
+ m_launchScript =
+ minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
+ QStringList args = minecraftInstance->javaArguments();
+ QString allArgs = args.join(", ");
+ emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) +
+ "]\n\n",
+ MessageLevel::MeshMC);
+
+ auto javaPath =
+ FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
+
+ m_process.setProcessEnvironment(instance->createEnvironment());
+
+ // make detachable - this will keep the process running even if the object
+ // is destroyed
+ m_process.setDetachable(true);
+
+ auto classPath = minecraftInstance->getClassPath();
+ classPath.prepend(
+ FS::PathCombine(APPLICATION->getJarsPath(), "NewLaunch.jar"));
+
+ auto natPath = minecraftInstance->getNativePath();
+#ifdef Q_OS_WIN
+ if (!fitsInLocal8bit(natPath)) {
+ args << "-Djava.library.path=" + shortPathName(natPath);
+ } else {
+ args << "-Djava.library.path=" + natPath;
+ }
+#else
+ args << "-Djava.library.path=" + natPath;
+#endif
+
+ args << "-cp";
+#ifdef Q_OS_WIN
+ QStringList processed;
+ for (auto& item : classPath) {
+ if (!fitsInLocal8bit(item)) {
+ processed << shortPathName(item);
+ } else {
+ processed << item;
+ }
+ }
+ args << processed.join(';');
+#else
+ args << classPath.join(':');
+#endif
+ args << "org.projecttick.EntryPoint";
+
+ qDebug() << args.join(' ');
+
+ QString wrapperCommandStr = instance->getWrapperCommand().trimmed();
+ if (!wrapperCommandStr.isEmpty()) {
+ auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr);
+ auto wrapperCommand = wrapperArgs.takeFirst();
+ auto realWrapperCommand =
+ QStandardPaths::findExecutable(wrapperCommand);
+ if (realWrapperCommand.isEmpty()) {
+ const char* reason =
+ QT_TR_NOOP("The wrapper command \"%1\" couldn't be found.");
+ emit logLine(QString(reason).arg(wrapperCommand),
+ MessageLevel::Fatal);
+ emitFailed(tr(reason).arg(wrapperCommand));
+ return;
+ }
+ emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n",
+ MessageLevel::MeshMC);
+ args.prepend(javaPath);
+ m_process.start(wrapperCommand, wrapperArgs + args);
+ } else {
+ m_process.start(javaPath, args);
+ }
+}
+
+void MeshMCPartLaunch::on_state(LoggedProcess::State state)
+{
+ switch (state) {
+ case LoggedProcess::FailedToStart: {
+ //: Error message displayed if instace can't start
+ const char* reason = QT_TR_NOOP("Could not launch minecraft!");
+ emit logLine(reason, MessageLevel::Fatal);
+ emitFailed(tr(reason));
+ return;
+ }
+ case LoggedProcess::Aborted:
+ case LoggedProcess::Crashed: {
+ m_parent->setPid(-1);
+ emitFailed(tr("Game crashed."));
+ return;
+ }
+ case LoggedProcess::Finished: {
+ m_parent->setPid(-1);
+ // if the exit code wasn't 0, report this as a crash
+ auto exitCode = m_process.exitCode();
+ if (exitCode != 0) {
+ emitFailed(tr("Game crashed."));
+ return;
+ }
+ // FIXME: make this work again
+ // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE",
+ // QString(exitCode)); run post-exit
+ emitSucceeded();
+ break;
+ }
+ case LoggedProcess::Running:
+ emit logLine(QString("Minecraft process ID: %1\n\n")
+ .arg(m_process.processId()),
+ MessageLevel::MeshMC);
+ m_parent->setPid(m_process.processId());
+ m_parent->instance()->setLastLaunch();
+ // send the launch script to MeshMC part
+ m_process.write(m_launchScript.toUtf8());
+
+ mayProceed = true;
+ emit readyForLaunch();
+ break;
+ default:
+ break;
+ }
+}
+
+void MeshMCPartLaunch::setWorkingDirectory(const QString& wd)
+{
+ m_process.setWorkingDirectory(wd);
+}
+
+void MeshMCPartLaunch::proceed()
+{
+ if (mayProceed) {
+ QString launchString("launch\n");
+ m_process.write(launchString.toUtf8());
+ mayProceed = false;
+ }
+}
+
+bool MeshMCPartLaunch::abort()
+{
+ if (mayProceed) {
+ mayProceed = false;
+ QString launchString("abort\n");
+ m_process.write(launchString.toUtf8());
+ } else {
+ auto state = m_process.state();
+ if (state == LoggedProcess::Running ||
+ state == LoggedProcess::Starting) {
+ m_process.kill();
+ }
+ }
+ return true;
+}
diff --git a/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.h b/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.h
new file mode 100644
index 0000000000..ff4285365f
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/MeshMCPartLaunch.h
@@ -0,0 +1,83 @@
+/* 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 <minecraft/auth/AuthSession.h>
+
+#include "MinecraftServerTarget.h"
+
+class MeshMCPartLaunch : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit MeshMCPartLaunch(LaunchTask* parent);
+ virtual ~MeshMCPartLaunch() {};
+
+ virtual void executeTask();
+ virtual bool abort();
+ virtual void proceed();
+ virtual bool canAbort() const
+ {
+ return true;
+ }
+ void setWorkingDirectory(const QString& wd);
+ void setAuthSession(AuthSessionPtr session)
+ {
+ m_session = session;
+ }
+
+ void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
+ {
+ m_serverToJoin = std::move(serverToJoin);
+ }
+
+ private slots:
+ void on_state(LoggedProcess::State state);
+
+ private:
+ LoggedProcess m_process;
+ QString m_command;
+ AuthSessionPtr m_session;
+ QString m_launchScript;
+ MinecraftServerTargetPtr m_serverToJoin;
+
+ bool mayProceed = false;
+};
diff --git a/meshmc/launcher/minecraft/launch/MinecraftServerTarget.cpp b/meshmc/launcher/minecraft/launch/MinecraftServerTarget.cpp
new file mode 100644
index 0000000000..a3443ce6a0
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/MinecraftServerTarget.cpp
@@ -0,0 +1,85 @@
+/* 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 "MinecraftServerTarget.h"
+
+#include <QStringList>
+
+// FIXME: the way this is written, it can't ever do any sort of validation and
+// can accept total junk
+MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress)
+{
+ QStringList split = fullAddress.split(":");
+
+ // The logic below replicates the exact logic minecraft uses for parsing
+ // server addresses. While the conversion is not lossless and eats errors,
+ // it ensures the same behavior within Minecraft and MeshMC when entering
+ // server addresses.
+ if (fullAddress.startsWith("[")) {
+ int bracket = fullAddress.indexOf("]");
+ if (bracket > 0) {
+ QString ipv6 = fullAddress.mid(1, bracket - 1);
+ QString port = fullAddress.mid(bracket + 1).trimmed();
+
+ if (port.startsWith(":") && !ipv6.isEmpty()) {
+ port = port.mid(1);
+ split = QStringList({ipv6, port});
+ } else {
+ split = QStringList({ipv6});
+ }
+ }
+ }
+
+ if (split.size() > 2) {
+ split = QStringList({fullAddress});
+ }
+
+ QString realAddress = split[0];
+
+ quint16 realPort = 25565;
+ if (split.size() > 1) {
+ bool ok;
+ realPort = split[1].toUInt(&ok);
+
+ if (!ok) {
+ realPort = 25565;
+ }
+ }
+
+ return MinecraftServerTarget{realAddress, realPort};
+}
diff --git a/meshmc/launcher/minecraft/launch/MinecraftServerTarget.h b/meshmc/launcher/minecraft/launch/MinecraftServerTarget.h
new file mode 100644
index 0000000000..ae453d637d
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/MinecraftServerTarget.h
@@ -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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <QString>
+
+struct MinecraftServerTarget {
+ QString address;
+ quint16 port;
+
+ static MinecraftServerTarget parse(const QString& fullAddress);
+};
+
+typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr;
diff --git a/meshmc/launcher/minecraft/launch/ModMinecraftJar.cpp b/meshmc/launcher/minecraft/launch/ModMinecraftJar.cpp
new file mode 100644
index 0000000000..72da25ec64
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ModMinecraftJar.cpp
@@ -0,0 +1,103 @@
+/* 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 "ModMinecraftJar.h"
+#include "launch/LaunchTask.h"
+#include "MMCZip.h"
+#include "minecraft/OpSys.h"
+#include "FileSystem.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+
+void ModMinecraftJar::executeTask()
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+
+ if (!m_inst->getJarMods().size()) {
+ emitSucceeded();
+ return;
+ }
+ // nuke obsolete stripped jar(s) if needed
+ if (!FS::ensureFolderPathExists(m_inst->binRoot())) {
+ emitFailed(tr("Couldn't create the bin folder for Minecraft.jar"));
+ }
+
+ auto finalJarPath =
+ QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
+ if (!removeJar()) {
+ emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath));
+ }
+
+ // create temporary modded jar, if needed
+ auto components = m_inst->getPackProfile();
+ auto profile = components->getProfile();
+ auto jarMods = m_inst->getJarMods();
+ if (jarMods.size()) {
+ auto mainJar = profile->getMainJar();
+ QStringList jars, temp1, temp2, temp3, temp4;
+ mainJar->getApplicableFiles(currentSystem, jars, temp1, temp2, temp3,
+ m_inst->getLocalLibraryPath());
+ auto sourceJarPath = jars[0];
+ if (!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) {
+ emitFailed(tr("Failed to create the custom Minecraft jar file."));
+ return;
+ }
+ }
+ emitSucceeded();
+}
+
+void ModMinecraftJar::finalize()
+{
+ removeJar();
+}
+
+bool ModMinecraftJar::removeJar()
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+ auto finalJarPath =
+ QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar");
+ QFile finalJar(finalJarPath);
+ if (finalJar.exists()) {
+ if (!finalJar.remove()) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/meshmc/launcher/minecraft/launch/ModMinecraftJar.h b/meshmc/launcher/minecraft/launch/ModMinecraftJar.h
new file mode 100644
index 0000000000..9453dd233e
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ModMinecraftJar.h
@@ -0,0 +1,60 @@
+/* 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 <memory>
+
+class ModMinecraftJar : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit ModMinecraftJar(LaunchTask* parent) : LaunchStep(parent) {};
+ virtual ~ModMinecraftJar() {};
+
+ virtual void executeTask() override;
+ virtual bool canAbort() const override
+ {
+ return false;
+ }
+ void finalize() override;
+
+ private:
+ bool removeJar();
+};
diff --git a/meshmc/launcher/minecraft/launch/PrintInstanceInfo.cpp b/meshmc/launcher/minecraft/launch/PrintInstanceInfo.cpp
new file mode 100644
index 0000000000..b7fabeb606
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/PrintInstanceInfo.cpp
@@ -0,0 +1,166 @@
+/* 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 <fstream>
+#include <string>
+
+#include "PrintInstanceInfo.h"
+#include <launch/LaunchTask.h>
+
+#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
+namespace
+{
+#if defined(Q_OS_LINUX)
+ void probeProcCpuinfo(QStringList& log)
+ {
+ std::ifstream cpuin("/proc/cpuinfo");
+ for (std::string line; std::getline(cpuin, line);) {
+ if (strncmp(line.c_str(), "model name", 10) == 0) {
+ log << QString::fromStdString(
+ line.substr(13, std::string::npos));
+ break;
+ }
+ }
+ }
+
+ void runLspci(QStringList& log)
+ {
+ // FIXME: fixed size buffers...
+ char buff[512];
+ int gpuline = -1;
+ int cline = 0;
+ FILE* lspci = popen("lspci -k", "r");
+
+ if (!lspci)
+ return;
+
+ while (fgets(buff, 512, lspci) != NULL) {
+ std::string str(buff);
+ if (str.length() < 9)
+ continue;
+ if (str.substr(8, 3) == "VGA") {
+ gpuline = cline;
+ log << QString::fromStdString(
+ str.substr(35, std::string::npos));
+ }
+ if (gpuline > -1 && gpuline != cline) {
+ if (cline - gpuline < 3) {
+ log << QString::fromStdString(
+ str.substr(1, std::string::npos));
+ }
+ }
+ cline++;
+ }
+ pclose(lspci);
+ }
+#elif defined(Q_OS_FREEBSD)
+ void runSysctlHwModel(QStringList& log)
+ {
+ char buff[512];
+ FILE* hwmodel = popen("sysctl hw.model", "r");
+ while (fgets(buff, 512, hwmodel) != NULL) {
+ log << QString::fromUtf8(buff);
+ break;
+ }
+ pclose(hwmodel);
+ }
+
+ void runPciconf(QStringList& log)
+ {
+ char buff[512];
+ std::string strcard;
+ FILE* pciconf = popen("pciconf -lv -a vgapci0", "r");
+ while (fgets(buff, 512, pciconf) != NULL) {
+ if (strncmp(buff, " vendor", 10) == 0) {
+ std::string str(buff);
+ strcard.append(str.substr(str.find_first_of("'") + 1,
+ str.find_last_not_of("'") -
+ (str.find_first_of("'") + 2)));
+ strcard.append(" ");
+ } else if (strncmp(buff, " device", 10) == 0) {
+ std::string str2(buff);
+ strcard.append(str2.substr(str2.find_first_of("'") + 1,
+ str2.find_last_not_of("'") -
+ (str2.find_first_of("'") + 2)));
+ }
+ log << QString::fromStdString(strcard);
+ break;
+ }
+ pclose(pciconf);
+ }
+#endif
+ void runGlxinfo(QStringList& log)
+ {
+ // FIXME: fixed size buffers...
+ char buff[512];
+ FILE* glxinfo = popen("glxinfo", "r");
+ if (!glxinfo)
+ return;
+
+ while (fgets(buff, 512, glxinfo) != NULL) {
+ if (strncmp(buff, "OpenGL version string:", 22) == 0) {
+ log << QString::fromUtf8(buff);
+ break;
+ }
+ }
+ pclose(glxinfo);
+ }
+
+} // namespace
+#endif
+
+void PrintInstanceInfo::executeTask()
+{
+ auto instance = m_parent->instance();
+ QStringList log;
+
+#if defined(Q_OS_LINUX)
+ ::probeProcCpuinfo(log);
+ ::runLspci(log);
+ ::runGlxinfo(log);
+#elif defined(Q_OS_FREEBSD)
+ ::runSysctlHwModel(log);
+ ::runPciconf(log);
+ ::runGlxinfo(log);
+#endif
+
+ logLines(log, MessageLevel::MeshMC);
+ logLines(instance->verboseDescription(m_session, m_serverToJoin),
+ MessageLevel::MeshMC);
+ emitSucceeded();
+}
diff --git a/meshmc/launcher/minecraft/launch/PrintInstanceInfo.h b/meshmc/launcher/minecraft/launch/PrintInstanceInfo.h
new file mode 100644
index 0000000000..572c5c84c7
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/PrintInstanceInfo.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 <memory>
+#include "minecraft/auth/AuthSession.h"
+#include "minecraft/launch/MinecraftServerTarget.h"
+
+// FIXME: temporary wrapper for existing task.
+class PrintInstanceInfo : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session,
+ MinecraftServerTargetPtr serverToJoin)
+ : LaunchStep(parent), m_session(session),
+ m_serverToJoin(serverToJoin) {};
+ virtual ~PrintInstanceInfo() {};
+
+ virtual void executeTask();
+ virtual bool canAbort() const
+ {
+ return false;
+ }
+
+ private:
+ AuthSessionPtr m_session;
+ MinecraftServerTargetPtr m_serverToJoin;
+};
diff --git a/meshmc/launcher/minecraft/launch/ReconstructAssets.cpp b/meshmc/launcher/minecraft/launch/ReconstructAssets.cpp
new file mode 100644
index 0000000000..95c9bedda3
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ReconstructAssets.cpp
@@ -0,0 +1,61 @@
+/* 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 "ReconstructAssets.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/PackProfile.h"
+#include "minecraft/AssetsUtils.h"
+#include "launch/LaunchTask.h"
+
+void ReconstructAssets::executeTask()
+{
+ auto instance = m_parent->instance();
+ std::shared_ptr<MinecraftInstance> minecraftInstance =
+ std::dynamic_pointer_cast<MinecraftInstance>(instance);
+ auto components = minecraftInstance->getPackProfile();
+ auto profile = components->getProfile();
+ auto assets = profile->getMinecraftAssets();
+
+ if (!AssetsUtils::reconstructAssets(assets->id,
+ minecraftInstance->resourcesDir())) {
+ emit logLine("Failed to reconstruct Minecraft assets.",
+ MessageLevel::Error);
+ }
+
+ emitSucceeded();
+}
diff --git a/meshmc/launcher/minecraft/launch/ReconstructAssets.h b/meshmc/launcher/minecraft/launch/ReconstructAssets.h
new file mode 100644
index 0000000000..1f080485c0
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ReconstructAssets.h
@@ -0,0 +1,56 @@
+/* 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 <memory>
+
+class ReconstructAssets : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit ReconstructAssets(LaunchTask* parent) : LaunchStep(parent) {};
+ virtual ~ReconstructAssets() {};
+
+ void executeTask() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+};
diff --git a/meshmc/launcher/minecraft/launch/ScanModFolders.cpp b/meshmc/launcher/minecraft/launch/ScanModFolders.cpp
new file mode 100644
index 0000000000..e9829718c7
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ScanModFolders.cpp
@@ -0,0 +1,85 @@
+/* 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 "ScanModFolders.h"
+#include "launch/LaunchTask.h"
+#include "MMCZip.h"
+#include "minecraft/OpSys.h"
+#include "FileSystem.h"
+#include "minecraft/MinecraftInstance.h"
+#include "minecraft/mod/ModFolderModel.h"
+
+void ScanModFolders::executeTask()
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+
+ auto loaders = m_inst->loaderModList();
+ connect(loaders.get(), &ModFolderModel::updateFinished, this,
+ &ScanModFolders::modsDone);
+ if (!loaders->update()) {
+ m_modsDone = true;
+ }
+
+ auto cores = m_inst->coreModList();
+ connect(cores.get(), &ModFolderModel::updateFinished, this,
+ &ScanModFolders::coreModsDone);
+ if (!cores->update()) {
+ m_coreModsDone = true;
+ }
+ checkDone();
+}
+
+void ScanModFolders::modsDone()
+{
+ m_modsDone = true;
+ checkDone();
+}
+
+void ScanModFolders::coreModsDone()
+{
+ m_coreModsDone = true;
+ checkDone();
+}
+
+void ScanModFolders::checkDone()
+{
+ if (m_modsDone && m_coreModsDone) {
+ emitSucceeded();
+ }
+}
diff --git a/meshmc/launcher/minecraft/launch/ScanModFolders.h b/meshmc/launcher/minecraft/launch/ScanModFolders.h
new file mode 100644
index 0000000000..e572022c90
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/ScanModFolders.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 <memory>
+
+class ScanModFolders : public LaunchStep
+{
+ Q_OBJECT
+ public:
+ explicit ScanModFolders(LaunchTask* parent) : LaunchStep(parent) {};
+ virtual ~ScanModFolders() {};
+
+ virtual void executeTask() override;
+ virtual bool canAbort() const override
+ {
+ return false;
+ }
+ private slots:
+ void coreModsDone();
+ void modsDone();
+
+ private:
+ void checkDone();
+
+ private: // DATA
+ bool m_modsDone = false;
+ bool m_coreModsDone = false;
+};
diff --git a/meshmc/launcher/minecraft/launch/VerifyJavaInstall.cpp b/meshmc/launcher/minecraft/launch/VerifyJavaInstall.cpp
new file mode 100644
index 0000000000..0f58b5efa4
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/VerifyJavaInstall.cpp
@@ -0,0 +1,374 @@
+/* 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 "VerifyJavaInstall.h"
+
+#include <QDir>
+#include <QDirIterator>
+#include <QFileInfo>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QProcess>
+#include <QRegularExpression>
+
+#include <launch/LaunchTask.h>
+#include <minecraft/MinecraftInstance.h>
+#include <minecraft/PackProfile.h>
+#include <minecraft/VersionFilterData.h>
+
+#include "Application.h"
+#include "FileSystem.h"
+#include "Json.h"
+#include "java/JavaUtils.h"
+
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+#include "BuildConfig.h"
+#include "net/Download.h"
+#endif
+
+#ifdef major
+#undef major
+#endif
+#ifdef minor
+#undef minor
+#endif
+
+namespace
+{
+ std::optional<JavaVersion> probeJavaVersion(const QString& javaPath)
+ {
+ const auto checkerJar =
+ FS::PathCombine(APPLICATION->getJarsPath(), "JavaCheck.jar");
+ if (!QFileInfo::exists(checkerJar)) {
+ return std::nullopt;
+ }
+
+ QProcess process;
+ process.setProgram(javaPath);
+ process.setArguments({"-jar", checkerJar});
+ process.setProcessEnvironment(CleanEnviroment());
+ process.setProcessChannelMode(QProcess::SeparateChannels);
+ process.start();
+
+ if (!process.waitForFinished(15000) ||
+ process.exitStatus() != QProcess::NormalExit ||
+ process.exitCode() != 0) {
+ return std::nullopt;
+ }
+
+ const auto stdoutData =
+ QString::fromLocal8Bit(process.readAllStandardOutput());
+ const auto lines = stdoutData.split('\n', Qt::SkipEmptyParts);
+ for (const auto& rawLine : lines) {
+ const auto line = rawLine.trimmed();
+ if (!line.startsWith("java.version=")) {
+ continue;
+ }
+ return JavaVersion(line.mid(QString("java.version=").size()));
+ }
+
+ return std::nullopt;
+ }
+} // namespace
+
+int VerifyJavaInstall::determineRequiredJavaMajor() const
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+ auto minecraftComponent =
+ m_inst->getPackProfile()->getComponent("net.minecraft");
+
+ if (minecraftComponent->getReleaseDateTime() >=
+ g_VersionFilterData.java25BeginsDate)
+ return 25;
+ if (minecraftComponent->getReleaseDateTime() >=
+ g_VersionFilterData.java21BeginsDate)
+ return 21;
+ if (minecraftComponent->getReleaseDateTime() >=
+ g_VersionFilterData.java17BeginsDate)
+ return 17;
+ if (minecraftComponent->getReleaseDateTime() >=
+ g_VersionFilterData.java16BeginsDate)
+ return 16;
+ if (minecraftComponent->getReleaseDateTime() >=
+ g_VersionFilterData.java8BeginsDate)
+ return 8;
+ return 0;
+}
+
+QString VerifyJavaInstall::javaInstallDir() const
+{
+ return JavaUtils::managedJavaRoot();
+}
+
+QString VerifyJavaInstall::findInstalledJava(int requiredMajor) const
+{
+ JavaUtils javaUtils;
+ QList<QString> systemJavas = javaUtils.FindJavaPaths();
+ QSet<QString> seenPaths;
+ for (const QString& javaPath : systemJavas) {
+ QString resolved = FS::ResolveExecutable(javaPath);
+ if (resolved.isEmpty() || seenPaths.contains(resolved))
+ continue;
+
+ seenPaths.insert(resolved);
+ const auto version = probeJavaVersion(resolved);
+ if (version.has_value() && version->major() >= requiredMajor) {
+ return resolved;
+ }
+ }
+
+ return {};
+}
+
+void VerifyJavaInstall::executeTask()
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+
+ auto javaVersion = m_inst->getJavaVersion();
+ int requiredMajor = determineRequiredJavaMajor();
+
+ // No Java requirement or already met
+ if (requiredMajor == 0 || javaVersion.major() >= requiredMajor) {
+ emitSucceeded();
+ return;
+ }
+
+ // Java version insufficient — try to find an already-downloaded one
+ emit logLine(
+ tr("Current Java version %1 does not meet the requirement of Java %2.")
+ .arg(javaVersion.toString())
+ .arg(requiredMajor),
+ MessageLevel::Warning);
+
+ QString existingJava = findInstalledJava(requiredMajor);
+ if (!existingJava.isEmpty()) {
+ emit logLine(tr("Found installed Java %1 at: %2")
+ .arg(requiredMajor)
+ .arg(existingJava),
+ MessageLevel::MeshMC);
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+ setJavaPathAndSucceed(existingJava);
+#else
+ m_inst->settings()->set("OverrideJavaLocation", true);
+ m_inst->settings()->set("JavaPath", existingJava);
+ emit logLine(tr("Java path set to: %1").arg(existingJava),
+ MessageLevel::MeshMC);
+ emitSucceeded();
+#endif
+ return;
+ }
+
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+ // Not found — auto-download
+ emit logLine(
+ tr("No installed Java %1 found. Downloading...").arg(requiredMajor),
+ MessageLevel::MeshMC);
+ autoDownloadJava(requiredMajor);
+#else
+ emitFailed(
+ tr("Java %1 is required but not installed. Please install it manually.")
+ .arg(requiredMajor));
+#endif
+}
+
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+void VerifyJavaInstall::autoDownloadJava(int requiredMajor)
+{
+ // Fetch version list from net.minecraft.java (Mojang)
+ fetchVersionList(requiredMajor);
+}
+
+void VerifyJavaInstall::fetchVersionList(int requiredMajor)
+{
+ m_fetchData.clear();
+ QString uid = "net.minecraft.java";
+ QString url = QString("%1%2/index.json").arg(BuildConfig.META_URL, uid);
+
+ m_fetchJob = new NetJob(tr("Fetch Java versions"), APPLICATION->network());
+ auto dl = Net::Download::makeByteArray(QUrl(url), &m_fetchData);
+ m_fetchJob->addNetAction(dl);
+
+ connect(
+ m_fetchJob.get(), &NetJob::succeeded, this,
+ [this, uid, requiredMajor]() {
+ m_fetchJob.reset();
+
+ QJsonDocument doc;
+ try {
+ doc = Json::requireDocument(m_fetchData);
+ } catch (const Exception& e) {
+ emitFailed(
+ tr("Failed to parse Java version list from meta server: %1")
+ .arg(e.cause()));
+ return;
+ }
+ if (!doc.isObject()) {
+ emitFailed(
+ tr("Failed to parse Java version list from meta server."));
+ return;
+ }
+
+ auto versions = JavaDownload::parseVersionIndex(doc.object(), uid);
+
+ // Find the matching version (e.g., "java25" for requiredMajor=25)
+ QString targetVersionId = QString("java%1").arg(requiredMajor);
+ bool found = false;
+ for (const auto& ver : versions) {
+ if (ver.versionId == targetVersionId) {
+ found = true;
+ fetchRuntimes(ver.versionId, requiredMajor);
+ return;
+ }
+ }
+
+ if (!found) {
+ emitFailed(tr("Java %1 is not available for download from "
+ "Mojang. Please install it manually.")
+ .arg(requiredMajor));
+ }
+ });
+
+ connect(m_fetchJob.get(), &NetJob::failed, this,
+ [this, requiredMajor](QString reason) {
+ emitFailed(tr("Failed to fetch Java version list: %1. Please "
+ "install Java %2 manually.")
+ .arg(reason)
+ .arg(requiredMajor));
+ m_fetchJob.reset();
+ });
+
+ m_fetchJob->start();
+}
+
+void VerifyJavaInstall::fetchRuntimes(const QString& versionId,
+ int requiredMajor)
+{
+ m_fetchData.clear();
+ QString uid = "net.minecraft.java";
+ QString url =
+ QString("%1%2/%3.json").arg(BuildConfig.META_URL, uid, versionId);
+
+ m_fetchJob =
+ new NetJob(tr("Fetch Java runtime details"), APPLICATION->network());
+ auto dl = Net::Download::makeByteArray(QUrl(url), &m_fetchData);
+ m_fetchJob->addNetAction(dl);
+
+ connect(m_fetchJob.get(), &NetJob::succeeded, this,
+ [this, requiredMajor]() {
+ auto fetchJob = std::move(m_fetchJob);
+
+ QJsonDocument doc;
+ try {
+ doc = Json::requireDocument(m_fetchData);
+ } catch (const Exception& e) {
+ emitFailed(tr("Failed to parse Java runtime details: %1")
+ .arg(e.cause()));
+ return;
+ }
+ if (!doc.isObject()) {
+ emitFailed(tr("Failed to parse Java runtime details."));
+ return;
+ }
+
+ auto allRuntimes = JavaDownload::parseRuntimes(doc.object());
+ QString myOS = JavaDownload::currentRuntimeOS();
+
+ // Filter for current platform
+ for (const auto& rt : allRuntimes) {
+ if (rt.runtimeOS == myOS) {
+ emit logLine(tr("Downloading %1 (%2)...")
+ .arg(rt.name, rt.version.toString()),
+ MessageLevel::MeshMC);
+ startDownload(rt, requiredMajor);
+ return;
+ }
+ }
+
+ emitFailed(tr("No Java %1 download available for your platform "
+ "(%2). Please install it manually.")
+ .arg(requiredMajor)
+ .arg(myOS));
+ });
+
+ connect(m_fetchJob.get(), &NetJob::failed, this, [this](QString reason) {
+ emitFailed(tr("Failed to fetch Java runtime details: %1").arg(reason));
+ m_fetchJob.reset();
+ });
+
+ m_fetchJob->start();
+}
+
+void VerifyJavaInstall::startDownload(const JavaDownload::RuntimeEntry& runtime,
+ int requiredMajor)
+{
+ QString dirName =
+ QString("%1-%2").arg(runtime.name, runtime.version.toString());
+ QString targetDir =
+ FS::PathCombine(javaInstallDir(), runtime.vendor, dirName);
+
+ m_downloadTask =
+ std::make_unique<JavaDownloadTask>(runtime, targetDir, this);
+
+ connect(m_downloadTask.get(), &Task::succeeded, this, [this]() {
+ QString javaPath = m_downloadTask->installedJavaPath();
+
+ if (javaPath.isEmpty()) {
+ emitFailed(
+ tr("Java was downloaded but the binary could not be found."));
+ return;
+ }
+
+ emit logLine(tr("Java downloaded and installed at: %1").arg(javaPath),
+ MessageLevel::MeshMC);
+ setJavaPathAndSucceed(javaPath);
+ });
+
+ connect(m_downloadTask.get(), &Task::failed, this,
+ [this, requiredMajor](const QString& reason) {
+ emitFailed(tr("Failed to download Java %1: %2")
+ .arg(requiredMajor)
+ .arg(reason));
+ m_downloadTask.reset();
+ });
+
+ connect(m_downloadTask.get(), &Task::status, this,
+ [this](const QString& status) {
+ emit logLine(status, MessageLevel::MeshMC);
+ });
+
+ m_downloadTask->start();
+}
+
+void VerifyJavaInstall::setJavaPathAndSucceed(const QString& javaPath)
+{
+ auto m_inst =
+ std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
+ // Set Java path override on the instance only, not globally
+ m_inst->settings()->set("OverrideJavaLocation", true);
+ m_inst->settings()->set("JavaPath", javaPath);
+ emit logLine(tr("Java path set to: %1").arg(javaPath),
+ MessageLevel::MeshMC);
+ emitSucceeded();
+}
+#endif
diff --git a/meshmc/launcher/minecraft/launch/VerifyJavaInstall.h b/meshmc/launcher/minecraft/launch/VerifyJavaInstall.h
new file mode 100644
index 0000000000..f54717e403
--- /dev/null
+++ b/meshmc/launcher/minecraft/launch/VerifyJavaInstall.h
@@ -0,0 +1,61 @@
+/* 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 <launch/LaunchStep.h>
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+#include "java/download/JavaRuntime.h"
+#include "java/download/JavaDownloadTask.h"
+#endif
+#include "net/NetJob.h"
+
+class VerifyJavaInstall : public LaunchStep
+{
+ Q_OBJECT
+
+ public:
+ explicit VerifyJavaInstall(LaunchTask* parent) : LaunchStep(parent) {};
+ ~VerifyJavaInstall() override = default;
+
+ void executeTask() override;
+ bool canAbort() const override
+ {
+ return false;
+ }
+
+ private:
+ int determineRequiredJavaMajor() const;
+ QString findInstalledJava(int requiredMajor) const;
+ QString javaInstallDir() const;
+#ifndef MeshMC_DISABLE_JAVA_DOWNLOADER
+ void autoDownloadJava(int requiredMajor);
+ void fetchVersionList(int requiredMajor);
+ void fetchRuntimes(const QString& versionId, int requiredMajor);
+ void startDownload(const JavaDownload::RuntimeEntry& runtime,
+ int requiredMajor);
+ void setJavaPathAndSucceed(const QString& javaPath);
+
+ NetJob::Ptr m_fetchJob;
+ QByteArray m_fetchData;
+ std::unique_ptr<JavaDownloadTask> m_downloadTask;
+#endif
+};