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 <noreply@anthropic.com>
This commit is contained in:
Shirona1337
2026-05-23 22:39:13 +08:00
parent 9a72330c85
commit e4ac90b69e
2 changed files with 30 additions and 8 deletions
+18 -3
View File
@@ -387,13 +387,28 @@ tasks.register('buildNative', Exec) {
} }
} }
tasks.register('packageDist', Copy) { tasks.register('packageDist') {
group = 'openzen' group = 'openzen'
description = 'Collect the Loader EXE into build/dist for distribution. ' + description = 'Collect the Loader EXE into build/dist for distribution. ' +
'OpenZen.dll is embedded inside the EXE, so we only ship one file.' 'OpenZen.dll is embedded inside the EXE, so we only ship one file.'
dependsOn 'buildNative' dependsOn 'buildNative'
from "$nativeBuildDir/loader/Release/OpenZenLoader.exe" // Use an inline copy { } block inside doLast rather than the Copy task
into "$buildDir/dist" // 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') { tasks.register('dll') {
+12 -5
View File
@@ -21,12 +21,19 @@ QString fromW(const std::wstring& w) {
return QString::fromWCharArray(w.c_str(), static_cast<int>(w.size())); return QString::fromWCharArray(w.c_str(), static_cast<int>(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 prefix = L"Minecraft";
static const std::wstring banned = L"Launcher";
if (title.size() < prefix.size()) return false; if (title.size() < prefix.size()) return false;
if (title.compare(0, prefix.size(), prefix) != 0) return false; return title.compare(0, prefix.size(), prefix) == 0;
return title.find(banned) == std::wstring::npos; }
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 } // namespace
@@ -167,7 +174,7 @@ void MainWindow::refreshNow() {
QVector<Row> filtered; QVector<Row> filtered;
filtered.reserve(procs.size()); filtered.reserve(procs.size());
for (const auto& jp : procs) { 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) }; Row r{ jp.pid, fromW(jp.window_title), fromW(jp.window_class) };
filtered.push_back(std::move(r)); filtered.push_back(std::move(r));
} }