diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:51:45 +0300 |
| commit | d3261e64152397db2dca4d691a990c6bc2a6f4dd (patch) | |
| tree | fac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/launcher/java/services | |
| parent | 31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff) | |
| download | Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip | |
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/launcher/java/services')
8 files changed, 1320 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/java/services/RuntimeCatalog.cpp b/archived/projt-launcher/launcher/java/services/RuntimeCatalog.cpp new file mode 100644 index 0000000000..f7fbc00602 --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeCatalog.cpp @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "java/services/RuntimeCatalog.hpp" + +#include <QtNetwork> +#include <QtXml> + +#include <QDebug> +#include <algorithm> + +#include "Application.h" +#include "java/services/RuntimeScanner.hpp" +#include "tasks/ConcurrentTask.h" + +namespace projt::java +{ + RuntimeCatalog::RuntimeCatalog(QObject* parent, Scope scope) : BaseVersionList(parent), m_scope(scope) + {} + + Task::Ptr RuntimeCatalog::getLoadTask() + { + load(); + return currentTask(); + } + + Task::Ptr RuntimeCatalog::currentTask() const + { + if (m_state == State::Loading) + { + return m_task; + } + return nullptr; + } + + void RuntimeCatalog::load() + { + if (m_state != State::Loading) + { + m_state = State::Loading; + m_task.reset(new RuntimeCatalogTask(this, m_scope)); + m_task->start(); + } + } + + const BaseVersion::Ptr RuntimeCatalog::at(int i) const + { + return m_entries.at(i); + } + + bool RuntimeCatalog::isLoaded() + { + return m_state == State::Ready; + } + + int RuntimeCatalog::count() const + { + return m_entries.count(); + } + + QVariant RuntimeCatalog::data(const QModelIndex& index, int role) const + { + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto runtime = std::dynamic_pointer_cast<RuntimeInstall>(m_entries[index.row()]); + switch (role) + { + case SortRole: return -index.row(); + case VersionPointerRole: return QVariant::fromValue(m_entries[index.row()]); + case VersionIdRole: return runtime->descriptor(); + case VersionRole: return runtime->version.toString(); + case RecommendedRole: return false; + case PathRole: return runtime->path; + case CPUArchitectureRole: return runtime->arch; + default: return QVariant(); + } + } + + BaseVersionList::RoleList RuntimeCatalog::providesRoles() const + { + return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole }; + } + + void RuntimeCatalog::updateListData(QList<BaseVersion::Ptr> versions) + { + beginResetModel(); + m_entries = std::move(versions); + sortVersions(); + endResetModel(); + m_state = State::Ready; + m_task.reset(); + } + + static bool sortRuntimes(BaseVersion::Ptr left, BaseVersion::Ptr right) + { + auto rleft = std::dynamic_pointer_cast<RuntimeInstall>(right); + auto rright = std::dynamic_pointer_cast<RuntimeInstall>(left); + return (*rleft) > (*rright); + } + + void RuntimeCatalog::sortVersions() + { + std::sort(m_entries.begin(), m_entries.end(), sortRuntimes); + } + + RuntimeCatalogTask::RuntimeCatalogTask(RuntimeCatalog* catalog, RuntimeCatalog::Scope scope) + : Task(), + m_catalog(catalog), + m_scope(scope) + {} + + void RuntimeCatalogTask::executeTask() + { + setStatus(tr("Detecting Java installations...")); + + RuntimeScanner scanner; + QStringList candidatePaths = scanner.collectPaths(m_scope == RuntimeCatalog::Scope::ManagedOnly); + + ConcurrentTask::Ptr job( + new ConcurrentTask("Runtime detection", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); + m_job.reset(job); + connect(m_job.get(), &Task::finished, this, &RuntimeCatalogTask::probeFinished); + connect(m_job.get(), &Task::progress, this, &Task::setProgress); + + qDebug() << "Probing the following runtime paths:"; + int token = 0; + for (const auto& candidate : candidatePaths) + { + RuntimeProbeTask::ProbeSettings settings; + settings.binaryPath = candidate; + settings.token = token; + auto probe = new RuntimeProbeTask(settings); + connect(probe, + &RuntimeProbeTask::probeFinished, + this, + [this](const RuntimeProbeTask::ProbeReport& report) { m_results << report; }); + job->addTask(Task::Ptr(probe)); + token++; + } + + m_job->start(); + } + + void RuntimeCatalogTask::probeFinished() + { + QList<RuntimeInstallPtr> candidates; + std::sort(m_results.begin(), + m_results.end(), + [](const RuntimeProbeTask::ProbeReport& a, const RuntimeProbeTask::ProbeReport& b) + { return a.token < b.token; }); + + qDebug() << "Found the following valid Java installations:"; + for (const auto& result : m_results) + { + if (result.status == RuntimeProbeTask::ProbeReport::Status::Valid) + { + RuntimeInstallPtr runtime(new RuntimeInstall()); + runtime->version = result.version; + runtime->arch = result.platformArch; + runtime->path = result.path; + runtime->vendor = result.vendor; + runtime->is_64bit = result.is_64bit; + runtime->managed = (m_scope == RuntimeCatalog::Scope::ManagedOnly); + candidates.append(runtime); + + qDebug() << " " << runtime->version.toString() << runtime->arch << runtime->path; + } + } + + QList<BaseVersion::Ptr> entries; + for (const auto& runtime : candidates) + { + BaseVersion::Ptr base = std::dynamic_pointer_cast<BaseVersion>(runtime); + if (base) + { + entries.append(runtime); + } + } + + m_catalog->updateListData(entries); + emitSucceeded(); + } +} // namespace projt::java diff --git a/archived/projt-launcher/launcher/java/services/RuntimeCatalog.hpp b/archived/projt-launcher/launcher/java/services/RuntimeCatalog.hpp new file mode 100644 index 0000000000..ddb2fc53a4 --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeCatalog.hpp @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#pragma once + +#include <QAbstractListModel> +#include <QObject> + +#include "BaseVersionList.h" +#include "QObjectPtr.h" +#include "java/core/RuntimeInstall.hpp" +#include "java/services/RuntimeProbeTask.hpp" +#include "tasks/Task.h" + +namespace projt::java +{ + class RuntimeCatalogTask; + + class RuntimeCatalog : public BaseVersionList + { + Q_OBJECT + enum class State + { + Idle, + Loading, + Ready + }; + + public: + enum class Scope + { + All, + ManagedOnly + }; + + explicit RuntimeCatalog(QObject* parent = nullptr, Scope scope = Scope::All); + + Task::Ptr getLoadTask() override; + bool isLoaded() override; + const BaseVersion::Ptr at(int i) const override; + int count() const override; + void sortVersions() override; + + QVariant data(const QModelIndex& index, int role) const override; + RoleList providesRoles() const override; + + public slots: + void updateListData(QList<BaseVersion::Ptr> versions) override; + + private: + void load(); + Task::Ptr currentTask() const; + + State m_state = State::Idle; + shared_qobject_ptr<RuntimeCatalogTask> m_task; + QList<BaseVersion::Ptr> m_entries; + Scope m_scope; + }; + + class RuntimeCatalogTask : public Task + { + Q_OBJECT + + public: + explicit RuntimeCatalogTask(RuntimeCatalog* catalog, RuntimeCatalog::Scope scope); + ~RuntimeCatalogTask() override = default; + + protected: + void executeTask() override; + + public slots: + void probeFinished(); + + private: + Task::Ptr m_job; + RuntimeCatalog* m_catalog = nullptr; + QList<RuntimeProbeTask::ProbeReport> m_results; + RuntimeCatalog::Scope m_scope; + }; +} // namespace projt::java diff --git a/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.cpp b/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.cpp new file mode 100644 index 0000000000..e6f027e462 --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.cpp @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "java/services/RuntimeEnvironment.hpp" + +#include <QDebug> + +#define IBUS "@im=ibus" + +namespace projt::java +{ + QString stripEnvEntries(const QString& name, const QString& value, const QString& remove) + { + QChar delimiter = ':'; +#ifdef Q_OS_WIN32 + delimiter = ';'; +#endif + + QStringList targetItems = value.split(delimiter); + QStringList toRemove = remove.split(delimiter); + + for (const QString& item : toRemove) + { + if (!targetItems.removeOne(item)) + { + qWarning() << "Entry" << item << "could not be stripped from variable" << name; + } + } + return targetItems.join(delimiter); + } + + QProcessEnvironment buildCleanEnvironment() + { + QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); + QProcessEnvironment env; + + QStringList ignored = { "JAVA_ARGS", "CLASSPATH", "CONFIGPATH", "JAVA_HOME", + "JRE_HOME", "_JAVA_OPTIONS", "JAVA_OPTIONS", "JAVA_TOOL_OPTIONS" }; + + QStringList stripped = { +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + "LD_LIBRARY_PATH", + "LD_PRELOAD", +#endif + "QT_PLUGIN_PATH", + "QT_FONTPATH" + }; + + for (const auto& key : rawenv.keys()) + { + auto current = rawenv.value(key); + if (ignored.contains(key)) + { + qDebug() << "Env: ignoring" << key << current; + continue; + } + if (key.startsWith("LAUNCHER_")) + { + qDebug() << "Env: ignoring" << key << current; + continue; + } + if (stripped.contains(key)) + { + QString cleaned = stripEnvEntries(key, current, rawenv.value("LAUNCHER_" + key)); + qDebug() << "Env: stripped" << key << current << "to" << cleaned; + current = cleaned; + } +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) + if (key == "XMODIFIERS" && current.contains(IBUS)) + { + QString saved = current; + current.replace(IBUS, ""); + qDebug() << "Env: stripped" << IBUS << "from" << saved << ":" << current; + } +#endif + env.insert(key, current); + } +#ifdef Q_OS_LINUX + if (!env.contains("LD_LIBRARY_PATH")) + { + env.insert("LD_LIBRARY_PATH", ""); + } +#endif + + return env; + } +} // namespace projt::java
\ No newline at end of file diff --git a/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.hpp b/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.hpp new file mode 100644 index 0000000000..7101fc51ad --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeEnvironment.hpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#pragma once + +#include <QProcessEnvironment> +#include <QString> + +namespace projt::java +{ + QString stripEnvEntries(const QString& name, const QString& value, const QString& remove); + QProcessEnvironment buildCleanEnvironment(); +} // namespace projt::java
\ No newline at end of file diff --git a/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.cpp b/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.cpp new file mode 100644 index 0000000000..b13b8d7ece --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.cpp @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "java/services/RuntimeProbeTask.hpp" + +#include <QDebug> +#include <QFile> +#include <QFileInfo> +#include <QMap> +#include <utility> + +#include "Application.h" +#include "Commandline.h" +#include "FileSystem.h" +#include "java/services/RuntimeEnvironment.hpp" + +namespace projt::java +{ + RuntimeProbeTask::RuntimeProbeTask(ProbeSettings settings) : Task(), m_settings(std::move(settings)) + {} + + QString RuntimeProbeTask::probeJarPath() + { + return APPLICATION->getJarPath("JavaCheck.jar"); + } + + void RuntimeProbeTask::executeTask() + { + QString checkerJar = probeJarPath(); + + if (checkerJar.isEmpty()) + { + qDebug() << "Java checker library could not be found. Please check your installation."; + ProbeReport report; + report.path = m_settings.binaryPath; + report.token = m_settings.token; + report.status = ProbeReport::Status::Errored; + emit probeFinished(report); + emitSucceeded(); + return; + } +#ifdef Q_OS_WIN + checkerJar = FS::getPathNameInLocal8bit(checkerJar); +#endif + + QStringList args; + process.reset(new QProcess()); + if (!m_settings.extraArgs.isEmpty()) + { + args.append(Commandline::splitArgs(m_settings.extraArgs)); + } + if (m_settings.minMem != 0) + { + args << QString("-Xms%1m").arg(m_settings.minMem); + } + if (m_settings.maxMem != 0) + { + args << QString("-Xmx%1m").arg(m_settings.maxMem); + } + if (m_settings.permGen != 64 && m_settings.permGen != 0) + { + args << QString("-XX:PermSize=%1m").arg(m_settings.permGen); + } + + args.append({ "-jar", checkerJar, "--list", "os.arch", "java.version", "java.vendor" }); + process->setArguments(args); + process->setProgram(m_settings.binaryPath); + process->setProcessChannelMode(QProcess::SeparateChannels); + process->setProcessEnvironment(buildCleanEnvironment()); + qDebug() << "Running runtime probe:" << m_settings.binaryPath << args.join(" "); + + connect(process.get(), &QProcess::finished, this, &RuntimeProbeTask::finished); + connect(process.get(), &QProcess::errorOccurred, this, &RuntimeProbeTask::error); + connect(process.get(), &QProcess::readyReadStandardOutput, this, &RuntimeProbeTask::stdoutReady); + connect(process.get(), &QProcess::readyReadStandardError, this, &RuntimeProbeTask::stderrReady); + connect(&killTimer, &QTimer::timeout, this, &RuntimeProbeTask::timeout); + killTimer.setSingleShot(true); + killTimer.start(15000); + process->start(); + } + + void RuntimeProbeTask::stdoutReady() + { + QByteArray data = process->readAllStandardOutput(); + QString added = QString::fromLocal8Bit(data); + added.remove('\r'); + m_stdout += added; + } + + void RuntimeProbeTask::stderrReady() + { + QByteArray data = process->readAllStandardError(); + QString added = QString::fromLocal8Bit(data); + added.remove('\r'); + m_stderr += added; + } + + void RuntimeProbeTask::finished(int exitcode, QProcess::ExitStatus status) + { + killTimer.stop(); + QProcessPtr activeProcess = process; + process.reset(); + + ProbeReport report; + report.path = m_settings.binaryPath; + report.token = m_settings.token; + report.stderrLog = m_stderr; + report.stdoutLog = m_stdout; + qDebug() << "STDOUT" << m_stdout; + if (!m_stderr.isEmpty()) + { + qWarning() << "STDERR" << m_stderr; + } + qDebug() << "Runtime probe finished with status" << status << "exit code" << exitcode; + + if (status == QProcess::CrashExit) + { + report.status = ProbeReport::Status::Errored; + emit probeFinished(report); + emitSucceeded(); + return; + } + + auto parseBlocks = [](const QString& stdoutText) + { + QList<QMap<QString, QString>> blocks; + QMap<QString, QString> current; + const auto lines = stdoutText.split('\n'); + for (QString line : lines) + { + line = line.trimmed(); + if (line.isEmpty()) + { + if (!current.isEmpty()) + { + blocks.append(current); + current.clear(); + } + continue; + } + if (line.contains("/bedrock/strata")) + { + continue; + } + const auto eq = line.indexOf('='); + if (eq <= 0) + { + continue; + } + const auto key = line.left(eq).trimmed(); + const auto value = line.mid(eq + 1).trimmed(); + if (!key.isEmpty() && !value.isEmpty()) + { + current.insert(key, value); + } + } + if (!current.isEmpty()) + { + blocks.append(current); + } + return blocks; + }; + + auto resolvePath = [](const QString& path) + { + if (path.isEmpty()) + { + return QString(); + } + auto resolved = FS::ResolveExecutable(path); + QString chosen = resolved.isEmpty() ? path : resolved; + QFileInfo info(chosen); + if (info.exists()) + { + return info.canonicalFilePath(); + } + return chosen; + }; + + auto matchesProbeTarget = [](const QString& targetPath, const QString& candidatePath) + { + if (targetPath.isEmpty() || candidatePath.isEmpty()) + { + return false; + } + if (candidatePath == targetPath) + { + return true; + } +#ifdef Q_OS_WIN + const QFileInfo targetInfo(targetPath); + const QFileInfo candidateInfo(candidatePath); + auto normalizeJavaName = [](QString fileName) + { + fileName = fileName.toLower(); + if (fileName == "javaw.exe") + { + return QStringLiteral("java.exe"); + } + return fileName; + }; + + return targetInfo.absolutePath().compare(candidateInfo.absolutePath(), Qt::CaseInsensitive) == 0 + && normalizeJavaName(targetInfo.fileName()) == normalizeJavaName(candidateInfo.fileName()); +#else + return false; +#endif + }; + + const auto targetPath = resolvePath(m_settings.binaryPath); + QMap<QString, QString> results; + auto blocks = parseBlocks(m_stdout); + for (const auto& block : blocks) + { + const auto candidatePath = resolvePath(block.value("java.path")); + if (matchesProbeTarget(targetPath, candidatePath)) + { + results = block; + break; + } + } + if (results.isEmpty() && !blocks.isEmpty() && targetPath.isEmpty()) + { + results = blocks.first(); + } + + if (results.isEmpty() || !results.contains("os.arch") || !results.contains("java.version") + || !results.contains("java.vendor")) + { + report.status = ProbeReport::Status::InvalidData; + emit probeFinished(report); + emitSucceeded(); + return; + } + + auto osArch = results["os.arch"]; + auto javaVersion = results["java.version"]; + auto javaVendor = results["java.vendor"]; + bool is64 = osArch == "x86_64" || osArch == "amd64" || osArch == "aarch64" || osArch == "arm64" + || osArch == "riscv64" || osArch == "ppc64le" || osArch == "ppc64"; + + report.status = ProbeReport::Status::Valid; + report.is_64bit = is64; + report.platformTag = is64 ? "64" : "32"; + report.platformArch = osArch; + report.version = javaVersion; + report.vendor = javaVendor; + qDebug() << "Runtime probe succeeded."; + emit probeFinished(report); + emitSucceeded(); + } + + void RuntimeProbeTask::error(QProcess::ProcessError err) + { + if (err == QProcess::FailedToStart) + { + qDebug() << "Runtime probe failed to start."; + qDebug() << "Process environment:"; + qDebug() << process->environment(); + qDebug() << "Native environment:"; + qDebug() << QProcessEnvironment::systemEnvironment().toStringList(); + killTimer.stop(); + ProbeReport report; + report.path = m_settings.binaryPath; + report.token = m_settings.token; + emit probeFinished(report); + } + emitSucceeded(); + } + + void RuntimeProbeTask::timeout() + { + if (process) + { + qDebug() << "Runtime probe has been killed by timeout."; + process->kill(); + } + } +} // namespace projt::java diff --git a/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.hpp b/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.hpp new file mode 100644 index 0000000000..c2b78a4c57 --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeProbeTask.hpp @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#pragma once + +#include <QProcess> +#include <QTimer> + +#include "QObjectPtr.h" +#include "java/core/RuntimeVersion.hpp" +#include "tasks/Task.h" + +namespace projt::java +{ + class RuntimeProbeTask : public Task + { + Q_OBJECT + public: + using QProcessPtr = shared_qobject_ptr<QProcess>; + using Ptr = shared_qobject_ptr<RuntimeProbeTask>; + + struct ProbeSettings + { + QString binaryPath; + QString extraArgs; + int minMem = 0; + int maxMem = 0; + int permGen = 0; + int token = 0; + }; + + struct ProbeReport + { + QString path; + int token = 0; + QString platformTag; + QString platformArch; + RuntimeVersion version; + QString vendor; + QString stdoutLog; + QString stderrLog; + bool is_64bit = false; + enum class Status + { + Errored, + InvalidData, + Valid + } status = Status::Errored; + }; + + explicit RuntimeProbeTask(ProbeSettings settings); + + static QString probeJarPath(); + + signals: + void probeFinished(const ProbeReport& report); + + protected: + void executeTask() override; + + private: + QProcessPtr process; + QTimer killTimer; + QString m_stdout; + QString m_stderr; + + ProbeSettings m_settings; + + private slots: + void timeout(); + void finished(int exitcode, QProcess::ExitStatus status); + void error(QProcess::ProcessError err); + void stdoutReady(); + void stderrReady(); + }; +} // namespace projt::java
\ No newline at end of file diff --git a/archived/projt-launcher/launcher/java/services/RuntimeScanner.cpp b/archived/projt-launcher/launcher/java/services/RuntimeScanner.cpp new file mode 100644 index 0000000000..5aed027d78 --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeScanner.cpp @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "java/services/RuntimeScanner.hpp" + +#include <QDir> +#include <QFileInfo> +#include <QProcessEnvironment> +#include <QStandardPaths> +#include <functional> +#include <vector> + +#include "Application.h" +#include "FileSystem.h" + +namespace projt::java +{ + static QStringList resolveExecutables(const QStringList& candidates) + { + QStringList resolved; + resolved.reserve(candidates.size()); + for (const auto& candidate : candidates) + { + if (candidate.isEmpty()) + { + continue; + } + if (QDir::isRelativePath(candidate)) + { + const auto found = QStandardPaths::findExecutable(candidate); + if (!found.isEmpty()) + { + resolved.append(QFileInfo(found).canonicalFilePath()); + } + continue; + } + const QFileInfo info(candidate); + if (info.exists() && info.isFile()) + { + resolved.append(info.canonicalFilePath()); + } + } + resolved.removeDuplicates(); + return resolved; + } + + QString RuntimeScanner::executableName() + { +#if defined(Q_OS_WIN32) + return QStringLiteral("javaw.exe"); +#else + return QStringLiteral("java"); +#endif + } + + QStringList RuntimeScanner::appendEnvPaths(const QStringList& base) const + { + QStringList expanded = base; + auto env = qEnvironmentVariable("PROJTLAUNCHER_JAVA_PATHS"); +#if defined(Q_OS_WIN32) + QStringList javaPaths = env.replace("\\", "/").split(QLatin1String(";"), Qt::SkipEmptyParts); + + auto envPath = qEnvironmentVariable("PATH"); + QStringList pathEntries = envPath.replace("\\", "/").split(QLatin1String(";"), Qt::SkipEmptyParts); + for (const QString& entry : pathEntries) + { + javaPaths.append(entry + "/" + executableName()); + } +#else + QStringList javaPaths = env.split(QLatin1String(":"), Qt::SkipEmptyParts); +#endif + for (const QString& entry : javaPaths) + { + expanded.append(entry); + } + return expanded; + } + +#if defined(Q_OS_WIN32) + QStringList RuntimeScanner::collectRegistryPaths(DWORD keyType, + const QString& keyName, + const QString& valueName, + const QString& suffix) const + { + QStringList entries; + + for (HKEY baseRegistry : { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) + { + HKEY rootKey; + if (RegOpenKeyExW(baseRegistry, + keyName.toStdWString().c_str(), + 0, + KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, + &rootKey) + != ERROR_SUCCESS) + { + continue; + } + + DWORD subKeyCount = 0; + RegQueryInfoKeyW(rootKey, NULL, NULL, NULL, &subKeyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (subKeyCount > 0) + { + for (DWORD i = 0; i < subKeyCount; i++) + { + WCHAR subKeyName[255]; + DWORD subKeyNameSize = 255; + auto retCode = RegEnumKeyExW(rootKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL); + if (retCode != ERROR_SUCCESS) + { + continue; + } + + QString versionKey = keyName + "\\" + QString::fromWCharArray(subKeyName) + suffix; + HKEY versionHandle; + if (RegOpenKeyExW(baseRegistry, + versionKey.toStdWString().c_str(), + 0, + KEY_READ | keyType, + &versionHandle) + != ERROR_SUCCESS) + { + continue; + } + + DWORD valueSize = 0; + if (RegQueryValueExW(versionHandle, valueName.toStdWString().c_str(), NULL, NULL, NULL, &valueSize) + == ERROR_SUCCESS) + { + std::vector<WCHAR> buffer(valueSize / sizeof(WCHAR) + 1, 0); + RegQueryValueExW(versionHandle, + valueName.toStdWString().c_str(), + NULL, + NULL, + reinterpret_cast<BYTE*>(buffer.data()), + &valueSize); + QString javaHome = QString::fromWCharArray(buffer.data()); + QString javaPath = QDir(FS::PathCombine(javaHome, "bin")).absoluteFilePath(executableName()); + entries.append(javaPath); + } + RegCloseKey(versionHandle); + } + } + + RegCloseKey(rootKey); + } + return entries; + } +#endif + + QStringList RuntimeScanner::collectManagedBundles() + { + QStringList bundles; + + auto addBundlePaths = [&bundles](const QString& prefix) + { + bundles.append(FS::PathCombine(prefix, "jre", "bin", RuntimeScanner::executableName())); + bundles.append(FS::PathCombine(prefix, "bin", RuntimeScanner::executableName())); + bundles.append(FS::PathCombine(prefix, RuntimeScanner::executableName())); + }; + + auto scanJavaDir = [&addBundlePaths](const QString& dirPath) + { + QDir dir(dirPath); + if (!dir.exists()) + { + return; + } + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& entry : entries) + { + addBundlePaths(entry.canonicalFilePath()); + } + }; + + scanJavaDir(APPLICATION->javaPath()); + + return bundles; + } + + QStringList RuntimeScanner::collectMinecraftBundles() + { + QStringList processPaths; +#if defined(Q_OS_MACOS) + processPaths << FS::PathCombine(QDir::homePath(), + FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); +#elif defined(Q_OS_WIN32) + auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", ""); + processPaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime"); + + auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); + auto minecraftStorePath = FS::PathCombine(QFileInfo(localAppDataPath).absoluteFilePath(), + "Packages", + "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); + processPaths << FS::PathCombine(minecraftStorePath, "LocalCache", "Local", "runtime"); +#else + processPaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); +#endif + + QStringList results; + while (!processPaths.isEmpty()) + { + auto dirPath = processPaths.takeFirst(); + QDir dir(dirPath); + if (!dir.exists()) + { + continue; + } + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + bool foundBin = false; + for (const auto& entry : entries) + { + if (entry.baseName() == "bin") + { + results.append(FS::PathCombine(entry.canonicalFilePath(), RuntimeScanner::executableName())); + foundBin = true; + break; + } + } + if (!foundBin) + { + for (const auto& entry : entries) + { + processPaths << entry.canonicalFilePath(); + } + } + } + return results; + } + + QStringList RuntimeScanner::collectPaths(bool managedOnly) const + { + QStringList candidates; + if (managedOnly) + { + candidates = collectManagedBundles(); + return resolveExecutables(candidates); + } + +#if defined(Q_OS_WIN32) + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome", "")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome", "")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome", "")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome", "")); + + candidates.append(collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome", "")); + candidates.append(collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome", "")); + candidates.append(collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome", "")); + candidates.append(collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome", "")); + + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI")); + + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI")); + + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI")); + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI")); + + candidates.append(collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI")); + candidates.append(collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI")); + candidates.append(collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI")); + candidates.append(collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI")); + + candidates.append(collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI")); + + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath", "")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath", "")); + + candidates.append( + collectRegistryPaths(KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath", "")); + candidates.append( + collectRegistryPaths(KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath", "")); + + candidates.append("C:/Program Files/Java/jre8/bin/javaw.exe"); + candidates.append("C:/Program Files/Java/jre7/bin/javaw.exe"); + candidates.append("C:/Program Files/Java/jre6/bin/javaw.exe"); + candidates.append("C:/Program Files (x86)/Java/jre8/bin/javaw.exe"); + candidates.append("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"); + candidates.append("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"); + + candidates.append("javaw"); +#elif defined(Q_OS_MAC) + candidates.append("java"); + candidates.append( + "/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); + candidates.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); + candidates.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); + + QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); + QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& java : libraryJVMJavas) + { + candidates.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + candidates.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); + } + + QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); + QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& java : systemLibraryJVMJavas) + { + candidates.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + candidates.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); + } + + auto home = qEnvironmentVariable("HOME"); + + QString sdkmanDir = qEnvironmentVariable("SDKMAN_DIR", FS::PathCombine(home, ".sdkman")); + QDir sdkmanJavaDir(FS::PathCombine(sdkmanDir, "candidates/java")); + QStringList sdkmanJavas = sdkmanJavaDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& java : sdkmanJavas) + { + candidates.append(sdkmanJavaDir.absolutePath() + "/" + java + "/bin/java"); + } + + QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); + QDir asdfJavaDir(FS::PathCombine(asdfDataDir, "installs/java")); + QStringList asdfJavas = asdfJavaDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& java : asdfJavas) + { + candidates.append(asdfJavaDir.absolutePath() + "/" + java + "/bin/java"); + } + + QDir userLibraryJVMDir(FS::PathCombine(home, "Library/Java/JavaVirtualMachines/")); + QStringList userLibraryJVMJavas = userLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& java : userLibraryJVMJavas) + { + candidates.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + candidates.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); + } +#elif defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD) + candidates.append("java"); + auto scanJavaDir = + [&candidates]( + const QString& dirPath, + const std::function<bool(const QFileInfo&)>& filter = [](const QFileInfo&) { return true; }) + { + QDir dir(dirPath); + if (!dir.exists()) + return; + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& entry : entries) + { + if (!filter(entry)) + continue; + QString prefix = entry.canonicalFilePath(); + candidates.append(FS::PathCombine(prefix, "jre/bin/java")); + candidates.append(FS::PathCombine(prefix, "bin/java")); + } + }; + auto snap = qEnvironmentVariable("SNAP"); + auto scanJavaDirs = [scanJavaDir, snap](const QString& dirPath) + { + scanJavaDir(dirPath); + if (!snap.isNull()) + { + scanJavaDir(snap + dirPath); + } + }; +#if defined(Q_OS_LINUX) + scanJavaDirs("/usr/java"); + scanJavaDirs("/usr/lib/jvm"); + scanJavaDirs("/usr/lib64/jvm"); + scanJavaDirs("/usr/lib32/jvm"); + auto gentooFilter = [](const QFileInfo& info) + { + QString fileName = info.fileName(); + return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-"); + }; + auto aoscFilter = [](const QFileInfo& info) + { + QString fileName = info.fileName(); + return fileName == "java" || fileName.startsWith("java-"); + }; + scanJavaDir("/usr/lib64", gentooFilter); + scanJavaDir("/usr/lib", gentooFilter); + scanJavaDir("/opt", gentooFilter); + scanJavaDir("/usr/lib", aoscFilter); + scanJavaDirs("java"); + scanJavaDirs("/opt/jdk"); + scanJavaDirs("/opt/jdks"); + scanJavaDirs("/opt/ibm"); + scanJavaDirs("/app/jdk"); +#elif defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD) + scanJavaDirs("/usr/local"); +#endif + auto home = qEnvironmentVariable("HOME"); + scanJavaDirs(FS::PathCombine(home, ".jdks")); + QString sdkmanDir = qEnvironmentVariable("SDKMAN_DIR", FS::PathCombine(home, ".sdkman")); + scanJavaDirs(FS::PathCombine(sdkmanDir, "candidates/java")); + QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); + scanJavaDirs(FS::PathCombine(asdfDataDir, "installs/java")); + scanJavaDirs(FS::PathCombine(home, ".gradle/jdks")); +#else + candidates.append("java"); +#endif + + candidates.append(collectMinecraftBundles()); + candidates.append(collectManagedBundles()); + candidates = appendEnvPaths(candidates); + return resolveExecutables(candidates); + } +} // namespace projt::java diff --git a/archived/projt-launcher/launcher/java/services/RuntimeScanner.hpp b/archived/projt-launcher/launcher/java/services/RuntimeScanner.hpp new file mode 100644 index 0000000000..09000c914d --- /dev/null +++ b/archived/projt-launcher/launcher/java/services/RuntimeScanner.hpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * 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, version 3. + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#pragma once + +#include <QStringList> + +#if defined(Q_OS_WIN32) +#include <windows.h> +#endif + +namespace projt::java +{ + class RuntimeScanner + { + public: + RuntimeScanner() = default; + + QStringList collectPaths(bool managedOnly) const; + + static QString executableName(); + static QStringList collectManagedBundles(); + static QStringList collectMinecraftBundles(); + + private: + QStringList appendEnvPaths(const QStringList& base) const; + +#if defined(Q_OS_WIN32) + QStringList collectRegistryPaths(DWORD keyType, + const QString& keyName, + const QString& valueName, + const QString& suffix) const; +#endif + }; +} // namespace projt::java |
