From e4ac90b69ee46e9f8dd594e1741e364cd6753d3d Mon Sep 17 00:00:00 2001 From: Shirona1337 Date: Sat, 23 May 2026 22:39:13 +0800 Subject: [PATCH] fix(loader): packageDist NO-SOURCE on clean + relax Minecraft filter build.gradle - Rewrite packageDist: drop the Copy task type and use a plain task with `copy { from src; into ... }` inside doLast. The Copy task snapshots its source set at configuration time, so a first `./gradlew clean dll` saw the source path before buildNative produced OpenZenLoader.exe and ended the task as NO-SOURCE. Doing the copy in doLast defers source resolution to after buildNative, which is when the EXE actually exists. - Bail with a clear GradleException if the EXE is still missing after buildNative instead of silently leaving build/dist empty, and log the packaged size on success. native/loader/src/MainWindow.cpp - Loosen the in-game window filter to: title startsWith "Minecraft" OR window class equals "GLFW30" (case-insensitive). GLFW30 is the LWJGL3 window class Minecraft uses before the title is set, so this catches the early-startup window too. Drop the "Launcher" blacklist - launchers are no longer matched by either branch. Co-Authored-By: Claude Opus 4.7 --- build.gradle | 21 ++++++++++++++++++--- native/loader/src/MainWindow.cpp | 17 ++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 9b89a44..6fa2eff 100644 --- a/build.gradle +++ b/build.gradle @@ -387,13 +387,28 @@ tasks.register('buildNative', Exec) { } } -tasks.register('packageDist', Copy) { +tasks.register('packageDist') { group = 'openzen' description = 'Collect the Loader EXE into build/dist for distribution. ' + 'OpenZen.dll is embedded inside the EXE, so we only ship one file.' dependsOn 'buildNative' - from "$nativeBuildDir/loader/Release/OpenZenLoader.exe" - into "$buildDir/dist" + // Use an inline copy { } block inside doLast rather than the Copy task + // type. Copy snapshots its source set at configuration time and reports + // NO-SOURCE on a first clean build because the EXE that buildNative is + // about to produce does not exist yet. doLast runs after buildNative, + // by which point the file is definitely on disk. + doLast { + def src = file("$nativeBuildDir/loader/Release/OpenZenLoader.exe") + if (!src.isFile()) { + throw new GradleException( + "Expected OpenZenLoader.exe at ${src} after buildNative; missing") + } + copy { + from src + into "$buildDir/dist" + } + logger.lifecycle("Packaged ${src.length()} bytes -> ${buildDir}/dist/${src.name}") + } } tasks.register('dll') { diff --git a/native/loader/src/MainWindow.cpp b/native/loader/src/MainWindow.cpp index cb7e5e0..79e086c 100644 --- a/native/loader/src/MainWindow.cpp +++ b/native/loader/src/MainWindow.cpp @@ -21,12 +21,19 @@ QString fromW(const std::wstring& w) { return QString::fromWCharArray(w.c_str(), static_cast(w.size())); } -bool isMinecraft(const std::wstring& title) { +bool startsWithMinecraft(const std::wstring& title) { static const std::wstring prefix = L"Minecraft"; - static const std::wstring banned = L"Launcher"; if (title.size() < prefix.size()) return false; - if (title.compare(0, prefix.size(), prefix) != 0) return false; - return title.find(banned) == std::wstring::npos; + return title.compare(0, prefix.size(), prefix) == 0; +} + +bool isMinecraft(const std::wstring& title, const std::wstring& cls) { + // Either the window title starts with "Minecraft" (in-game window + // labelled e.g. "Minecraft 1.20.1"), or the window class is "GLFW30" + // (the class Minecraft's LWJGL GLFW windows always use, even before the + // title has been set). + if (startsWithMinecraft(title)) return true; + return _wcsicmp(cls.c_str(), L"GLFW30") == 0; } } // namespace @@ -167,7 +174,7 @@ void MainWindow::refreshNow() { QVector filtered; filtered.reserve(procs.size()); for (const auto& jp : procs) { - if (!isMinecraft(jp.window_title)) continue; + if (!isMinecraft(jp.window_title, jp.window_class)) continue; Row r{ jp.pid, fromW(jp.window_title), fromW(jp.window_class) }; filtered.push_back(std::move(r)); }