Resolve conflicts with #122: keep both feature docs and CI steps, and drop
this branch's copy of the close-helper block, which main now starts after
the installed-client check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Resolve conflicts with #122: keep both feature docs and CI steps, and drop
this branch's copy of the close-helper block, which main now starts after
the installed-client check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- fail closed when X11 omits _NET_CLIENT_LIST or WM_CLASS, and recognize the
RiotClientServices window class;
- require stale client processes to be at least 60 seconds old;
- recheck process identity before SIGTERM and SIGKILL and recheck the window
before signalling;
- document the age and window-detection guards.
- fail closed when X11 omits _NET_CLIENT_LIST or WM_CLASS, and recognize the
RiotClientServices window class;
- require stale client processes to be at least 60 seconds old;
- recheck process identity before SIGTERM and SIGKILL and recheck the window
before signalling;
- document the age and window-detection guards.
- restore hidden windows even when the launcher output pipe is closed;
- exit the hide watcher after ten idle minutes and start it only after the
installed-client check;
- reject missing, malformed or invalid close-during-game preferences instead
of silently selecting hide;
- document the preference contract and idle behavior.
- restore hidden windows even when the launcher output pipe is closed;
- exit the hide watcher after ten idle minutes and start it only after the
installed-client check;
- reject missing, malformed or invalid close-during-game preferences instead
of silently selecting hide;
- document the preference contract and idle behavior.
A launcher can exit while its long-lived close/hide helper remains. The next
run then starts another watcher, so several copies watch the same prefix.
Add an opt-in RCLOL_CLEAN_STALE_HELPERS=1 preflight that removes only helper
processes of this prefix reparented to init/systemd. Helpers of a live
launcher, other prefixes, the game, Riot/League/CEF, darlingserver and the
runtime are preserved. Unreadable or relative process data leaves a process
alone.
Validated across repeated launcher starts: the cleanup removed four, then one,
then one orphaned watcher; a subsequent dry run reported no orphaned helpers.
Zombie reaping after partial recovery is tracked separately in #124.
A launcher can exit while its long-lived close/hide helper remains. The next
run then starts another watcher, so several copies watch the same prefix.
Add an opt-in RCLOL_CLEAN_STALE_HELPERS=1 preflight that removes only helper
processes of this prefix reparented to init/systemd. Helpers of a live
launcher, other prefixes, the game, Riot/League/CEF, darlingserver and the
runtime are preserved. Unreadable or relative process data leaves a process
alone.
Validated across repeated launcher starts: the cleanup removed four, then one,
then one orphaned watcher; a subsequent dry run reported no orphaned helpers.
Zombie reaping after partial recovery is tracked separately in #124.
Darlingserver normally stays alive after the Riot Client or game is closed.
If the client window disappears but Riot/League/CEF processes remain,
restarting via run.sh reuses a half-dead runtime and can fail to open a
window.
Add an opt-in RCLOL_RECOVER_STALE_CLIENT=1 preflight. When a darlingserver is
alive, no game is running, no managed client window is visible and stale
client processes belong to this prefix, stop only that client/CEF tree.
Preserve darlingserver, launchd/mldr, sockets and .init.pid, then continue
with the normal product launch.
The helper fails closed when X11 is unavailable, never touches a running game
or a visible client window, and does not patch client binaries, CEF or assets.
It saves the Darling bootstrap, but not Riot Client/CEF startup; a full runtime
restart remains the fallback if no window appears.
Darlingserver normally stays alive after the Riot Client or game is closed.
If the client window disappears but Riot/League/CEF processes remain,
restarting via run.sh reuses a half-dead runtime and can fail to open a
window.
Add an opt-in RCLOL_RECOVER_STALE_CLIENT=1 preflight. When a darlingserver is
alive, no game is running, no managed client window is visible and stale
client processes belong to this prefix, stop only that client/CEF tree.
Preserve darlingserver, launchd/mldr, sockets and .init.pid, then continue
with the normal product launch.
The helper fails closed when X11 is unavailable, never touches a running game
or a visible client window, and does not patch client binaries, CEF or assets.
It saves the Darling bootstrap, but not Riot Client/CEF startup; a full runtime
restart remains the fallback if no window appears.
Add RCLOL_CLIENT_IN_GAME_TRICKS=1 as the practical launcher switch. It reads
lol-user-experience from LCULocalPreferences.yaml:
- potatoModeEnabled false or unreadable: no action;
- closeLeagueClientDuringGame true: close the client UI;
- closeLeagueClientDuringGame false: hide the client windows.
The existing close and hide switches remain low-level options when the
automatic switch is not set. The hide helper accepts the master switch, and
the resolver plus preference cases are covered by tests and documentation.
The local .desktop entry was validated with the master switch; the installed
preferences select hide on this machine.
Add RCLOL_CLIENT_IN_GAME_TRICKS=1 as the practical launcher switch. It reads
lol-user-experience from LCULocalPreferences.yaml:
- potatoModeEnabled false or unreadable: no action;
- closeLeagueClientDuringGame true: close the client UI;
- closeLeagueClientDuringGame false: hide the client windows.
The existing close and hide switches remain low-level options when the
automatic switch is not set. The hide helper accepts the master switch, and
the resolver plus preference cases are covered by tests and documentation.
The local .desktop entry was validated with the master switch; the installed
preferences select hide on this machine.
Add an opt-in alternative to closing LeagueClientUx: hide the managed
LeagueClientUx and Riot Client X11 windows while the game process runs and
show them again when it exits. The client stays open; the game window is
never touched.
Detection is attributed to the launcher prefix through __mldr_sockpath and
fails closed when proc, ownership or window state is unreadable. The helper
requires X11/Xwayland, restores the window on end, signal or error, and is
mutually exclusive with RCLOL_CLOSE_CLIENT_IN_GAME=1.
Document the flag, compliance scope and limits. A local 30-second test on an
i5-3470/RX 550 reduced summed client/CEF CPU from 120.7% to 93.4%; this is a
CPU-bound-host measurement, not a universal FPS claim.
No client binaries, CEF, assets or anti-cheat are modified.
Add an opt-in alternative to closing LeagueClientUx: hide the managed
LeagueClientUx and Riot Client X11 windows while the game process runs and
show them again when it exits. The client stays open; the game window is
never touched.
Detection is attributed to the launcher prefix through __mldr_sockpath and
fails closed when proc, ownership or window state is unreadable. The helper
requires X11/Xwayland, restores the window on end, signal or error, and is
mutually exclusive with RCLOL_CLOSE_CLIENT_IN_GAME=1.
Document the flag, compliance scope and limits. A local 30-second test on an
i5-3470/RX 550 reduced summed client/CEF CPU from 120.7% to 93.4%; this is a
CPU-bound-host measurement, not a universal FPS claim.
No client binaries, CEF, assets or anti-cheat are modified.
Under the rootless NixOS launcher darlingserver cannot raise its hard
RLIMIT_NOFILE to nr_open, so it kept the soft limit it inherited. Every
named memory entry holds a memfd in darlingserver, and its descriptors
peak at about 1100 when the League lobby opens. With an inherited soft
limit of 1024, memfd_create failed with EMFILE, mach_make_memory_entry_64
returned KERN_RESOURCE_SHORTAGE, and Chromium's CHECK in
PlatformSharedMemoryRegion::Create killed the League UX and the Riot
Client renderer after clicking Play.
When raising the hard limit fails, raise the soft limit to the inherited
hard limit instead. Log errno, size and fd limits when named-entry
creation fails, and log the live memfd count every 1024 creations so a
leak shows up in dserver.log.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Under the rootless NixOS launcher darlingserver cannot raise its hard
RLIMIT_NOFILE to nr_open, so it kept the soft limit it inherited. Every
named memory entry holds a memfd in darlingserver, and its descriptors
peak at about 1100 when the League lobby opens. With an inherited soft
limit of 1024, memfd_create failed with EMFILE, mach_make_memory_entry_64
returned KERN_RESOURCE_SHORTAGE, and Chromium's CHECK in
PlatformSharedMemoryRegion::Create killed the League UX and the Riot
Client renderer after clicking Play.
When raising the hard limit fails, raise the soft limit to the inherited
hard limit instead. Log errno, size and fd limits when named-entry
creation fails, and log the live memfd count every 1024 creations so a
leak shows up in dserver.log.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A long patch or login can keep the League client closed for more than ten
minutes after launch; the helper then left before the first match.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A long patch or login can keep the League client closed for more than ten
minutes after launch; the helper then left before the first match.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mesa derives its shader disk cache from $HOME. Inside the guest that is
/Users/<user>, which does not exist on the host filesystem, so Mesa could not
create the cache and disabled it, once per process:
Failed to create /Users/<user> for shader cache (Permission denied)---disabling
One launcher log contained 212 of those lines, and no shader compiled by one
process was reused by another process or by the next start: the client and the
game recompiled every shader on every launch, spending CPU at startup for no
gain.
Mesa is host code loaded into the guest and reads MESA_SHADER_CACHE_DIR verbatim,
so point it at a host directory; Mesa creates mesa_shader_cache/ (client and game
shaders) and radv_builtin_shaders/ below it. A non-empty value from the caller is
preserved, and a directory that cannot be created leaves the previous behaviour
instead of ending the launch.
Observed on a Radeon RX 550 with radv: no shader-cache failure after the change,
and 434 files / 5.6 MB written during one client start, still growing while the
client ran. The cache is reused from the second start on, which lowers the
client's CPU use while it starts; the startup-time gain itself has not been
measured separately.
Mesa derives its shader disk cache from $HOME. Inside the guest that is
/Users/<user>, which does not exist on the host filesystem, so Mesa could not
create the cache and disabled it, once per process:
Failed to create /Users/<user> for shader cache (Permission denied)---disabling
One launcher log contained 212 of those lines, and no shader compiled by one
process was reused by another process or by the next start: the client and the
game recompiled every shader on every launch, spending CPU at startup for no
gain.
Mesa is host code loaded into the guest and reads MESA_SHADER_CACHE_DIR verbatim,
so point it at a host directory; Mesa creates mesa_shader_cache/ (client and game
shaders) and radv_builtin_shaders/ below it. A non-empty value from the caller is
preserved, and a directory that cannot be created leaves the previous behaviour
instead of ending the launch.
Observed on a Radeon RX 550 with radv: no shader-cache failure after the change,
and 434 files / 5.6 MB written during one client start, still growing while the
client ran. The cache is reused from the second start on, which lowers the
client's CPU use while it starts; the startup-time gain itself has not been
measured separately.
The client does not come back properly after a match when its own "Close client
during game" option is enabled. The UI (LeagueClientUx and its helpers) is a
Chromium/CEF instance and, under Darling, the pre-match instance keeps running:
the client the backend starts when the match ends is a second CEF instance on the
same --user-data-dir, and its window never paints. Its DOM and audio work and the
window stays black; closing the client by hand during a match leaves the same
state for the same reason.
RCLOL_CLOSE_CLIENT_IN_GAME=1 opts in to closing that UI from the host, and only
while a match runs, so the client that appears afterwards is the only instance.
The UI also competes with the game for CPU: on a four-core host with a Radeon
RX 550 a practice match reported roughly 120 FPS with the UI running and roughly
180 FPS with it closed. This is not the helper-killing memory optimization the
RAM section warns against, and the Riot client, the League backend and the game
are never touched.
The helper fails closed. It attributes the game and the client to the launcher's
prefix through the loader's __mldr_sockpath, since guest processes do not carry
DPREFIX (checked on a live session). It never closes a client younger than 60
seconds (the one the backend starts after a match), preserves a process of
another installation or owner, acts only when the environment and the stat fields
can be read, gives up after three closes in one match, and leaves after ten
minutes without a client or a match. It is off by default.
Tests use fake /proc entries and mock every signal.
The client does not come back properly after a match when its own "Close client
during game" option is enabled. The UI (LeagueClientUx and its helpers) is a
Chromium/CEF instance and, under Darling, the pre-match instance keeps running:
the client the backend starts when the match ends is a second CEF instance on the
same --user-data-dir, and its window never paints. Its DOM and audio work and the
window stays black; closing the client by hand during a match leaves the same
state for the same reason.
RCLOL_CLOSE_CLIENT_IN_GAME=1 opts in to closing that UI from the host, and only
while a match runs, so the client that appears afterwards is the only instance.
The UI also competes with the game for CPU: on a four-core host with a Radeon
RX 550 a practice match reported roughly 120 FPS with the UI running and roughly
180 FPS with it closed. This is not the helper-killing memory optimization the
RAM section warns against, and the Riot client, the League backend and the game
are never touched.
The helper fails closed. It attributes the game and the client to the launcher's
prefix through the loader's __mldr_sockpath, since guest processes do not carry
DPREFIX (checked on a live session). It never closes a client younger than 60
seconds (the one the backend starts after a match), preserves a process of
another installation or owner, acts only when the environment and the stat fields
can be read, gives up after three closes in one match, and leaves after ten
minutes without a client or a match. It is off by default.
Tests use fake /proc entries and mock every signal.
With DARLING_METAL_MAC_GPU_NAME=1, MTLDevice.name is the GPU the
MacBookPro16,1 that IOKit reports shipped with, from the host's vendor:
"AMD Radeon Pro 5500M" (vendor 0x1002) or "Intel UHD Graphics 630"
(0x8086). NVIDIA keeps its own name so the game never takes another
vendor's driver paths. recommendedMaxWorkingSetSize and every other value
stay the host's, which was the risky part of #61's identity.
The Riot launcher turns it on by default (0 or empty turns it off); other
Darling apps keep the host name. DARLING_METAL_GPU_NAME still wins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With DARLING_METAL_MAC_GPU_NAME=1, MTLDevice.name is the GPU the
MacBookPro16,1 that IOKit reports shipped with, from the host's vendor:
"AMD Radeon Pro 5500M" (vendor 0x1002) or "Intel UHD Graphics 630"
(0x8086). NVIDIA keeps its own name so the game never takes another
vendor's driver paths. recommendedMaxWorkingSetSize and every other value
stay the host's, which was the risky part of #61's identity.
The Riot launcher turns it on by default (0 or empty turns it off); other
Darling apps keep the host name. DARLING_METAL_GPU_NAME still wins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
From #61, without the default "AMD Radeon Pro 5500M" / 4 GB identity: the
host GPU's name and Vulkan working set stay the default for every app, and
the existing DARLING_METAL_GPU_NAME and DARLING_METAL_VRAM_MB overrides
remain the opt-in. Reporting 4 GB on a 2 GB card invites allocation
failures, and under-reports larger cards.
Kept from #61:
- registryID returns MTLDeviceInfo::kDefaultRegistryID instead of 0; a
real Mac never reports 0 and apps key devices by it.
- Vertex buffer layout and attribute arrays release the old item after
retiring it; the retire step read the item after the release could free it.
- newDepthStencilState, newSamplerState and the device name return nil (or
the fallback name) when Indium throws, instead of unwinding through
Objective-C.
- MTLDepthStencilGetIndium is declared only with DARLING_METAL_ENABLED.
- The replaceRegion trace probes at most `width` bytes, which stays inside
the first row for every pixel format.
metal.patch is regenerated in its existing file order, so the diff is
these changes only. tests/test_metal_device_info.py compiles the host
check against MTLDeviceInfo.h as shipped in the patch and runs in CI.
Split from #61.
Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
From #61, without the default "AMD Radeon Pro 5500M" / 4 GB identity: the
host GPU's name and Vulkan working set stay the default for every app, and
the existing DARLING_METAL_GPU_NAME and DARLING_METAL_VRAM_MB overrides
remain the opt-in. Reporting 4 GB on a 2 GB card invites allocation
failures, and under-reports larger cards.
Kept from #61:
- registryID returns MTLDeviceInfo::kDefaultRegistryID instead of 0; a
real Mac never reports 0 and apps key devices by it.
- Vertex buffer layout and attribute arrays release the old item after
retiring it; the retire step read the item after the release could free it.
- newDepthStencilState, newSamplerState and the device name return nil (or
the fallback name) when Indium throws, instead of unwinding through
Objective-C.
- MTLDepthStencilGetIndium is declared only with DARLING_METAL_ENABLED.
- The replaceRegion trace probes at most `width` bytes, which stays inside
the first row for every pixel format.
metal.patch is regenerated in its existing file order, so the diff is
these changes only. tests/test_metal_device_info.py compiles the host
check against MTLDeviceInfo.h as shipped in the patch and runs in CI.
Split from #61.
Co-authored-by: DevWebeloper <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>