Summary
When the game's .NET host fails with "You must install .NET to run this application" and a runtime of the right major version exists in a location the host did not search (/usr/lib/dotnet, ~/.dotnet, or anywhere else outside its own registered/default locations), the launcher should be able to set DOTNET_ROOT for the game process itself, instead of only pointing the player at the Linux install guide and asking them to register the location system-wide.
Why
Two reports from the same PikaOS player, both filed against the Linux install guide, ended up at the same root cause from two different angles. The .NET host only checks its own environment variables, a registered location file, and a compiled-in default; a runtime installed outside all three is invisible to it no matter how correctly it was installed. The guide (PR #510) now covers the registration files, but every player who lands in this state still has to notice the error, read the guide, and edit a file under /etc by hand before they can play. The launcher already has the runtime installed and already knows which major version the game needs: it can skip that whole detour for the common case.
How it could work here
src/ipc/handlers/gameExecutionOutcome.ts already recognizes the host's sentence (MISSING_DOTNET_SENTINEL, scanned through hasMissingDotnetSentinel), and src/ipc/handlers/gameHandlers.ts's EXECUTE_GAME already accumulates that scan from the spawned process's stderr and resolves the missing-dotnet reason from it.
Before launching, or right after a missing-dotnet failure, a probe could check a short list of candidate roots, in this order: $DOTNET_ROOT when the player set it, /usr/lib/dotnet (Debian and Ubuntu packages, and our own guide), /usr/share/dotnet (the host's default, kept for the mismatch case where the arch file points elsewhere), ~/.dotnet (the official install script's default), /home/linuxbrew/.linuxbrew/opt/dotnet/libexec and ~/.linuxbrew/opt/dotnet/libexec (Homebrew on Linux, the usual route on immutable distributions such as SteamOS, Bazzite or Fedora Silverblue, where nothing is packaged in the system locations; Homebrew's bin/dotnet is a symlink into that libexec folder, so resolve the link rather than pointing DOTNET_ROOT at bin), and the dotnet found on PATH resolved through its symlink as a last resort for shared/Microsoft.NETCore.App/<major>.* matching the major version the game build needs. If one matches, pass DOTNET_ROOT in the child process's environment. EXECUTE_GAME already builds that environment as { ...process.env, ...processEnv, ...plan.env } (gameHandlers.ts around the realGameProcess().run(...) call), so the probe's result would slot in there rather than needing a new pathway. Log only a reason token for which root got used, never the full path, matching the redaction the launcher already does elsewhere in these logs.
Never write to /etc/dotnet/install_location*: this stays a per-launch, per-process fix, not a system-wide change made on the player's behalf. The per-Installation ENV variables field already lets a player set DOTNET_ROOT by hand today; this issue is about the launcher doing that automatically for the common case, not about adding a new way to configure it.
Out of scope
Installing .NET for the player. This is about finding and pointing at a runtime that is already there, not about downloading or managing one.
Acceptance
- A domain rule with tests covering the candidate-root probe and the major-version match (including no match, and more than one candidate matching).
- An IPC test for
EXECUTE_GAME against a fake filesystem, covering the case where the probe finds a runtime and DOTNET_ROOT ends up in the spawned environment.
- A notification naming the root that was used, in en-US and fr-FR.
Reported on Discord by SatanicPanic (PikaOS); the Homebrew and immutable-distribution case by Valkyrien04 on this issue.
Summary
When the game's .NET host fails with "You must install .NET to run this application" and a runtime of the right major version exists in a location the host did not search (
/usr/lib/dotnet,~/.dotnet, or anywhere else outside its own registered/default locations), the launcher should be able to setDOTNET_ROOTfor the game process itself, instead of only pointing the player at the Linux install guide and asking them to register the location system-wide.Why
Two reports from the same PikaOS player, both filed against the Linux install guide, ended up at the same root cause from two different angles. The .NET host only checks its own environment variables, a registered location file, and a compiled-in default; a runtime installed outside all three is invisible to it no matter how correctly it was installed. The guide (PR #510) now covers the registration files, but every player who lands in this state still has to notice the error, read the guide, and edit a file under
/etcby hand before they can play. The launcher already has the runtime installed and already knows which major version the game needs: it can skip that whole detour for the common case.How it could work here
src/ipc/handlers/gameExecutionOutcome.tsalready recognizes the host's sentence (MISSING_DOTNET_SENTINEL, scanned throughhasMissingDotnetSentinel), andsrc/ipc/handlers/gameHandlers.ts'sEXECUTE_GAMEalready accumulates that scan from the spawned process's stderr and resolves themissing-dotnetreason from it.Before launching, or right after a
missing-dotnetfailure, a probe could check a short list of candidate roots, in this order:$DOTNET_ROOTwhen the player set it,/usr/lib/dotnet(Debian and Ubuntu packages, and our own guide),/usr/share/dotnet(the host's default, kept for the mismatch case where the arch file points elsewhere),~/.dotnet(the official install script's default),/home/linuxbrew/.linuxbrew/opt/dotnet/libexecand~/.linuxbrew/opt/dotnet/libexec(Homebrew on Linux, the usual route on immutable distributions such as SteamOS, Bazzite or Fedora Silverblue, where nothing is packaged in the system locations; Homebrew'sbin/dotnetis a symlink into thatlibexecfolder, so resolve the link rather than pointingDOTNET_ROOTatbin), and thedotnetfound onPATHresolved through its symlink as a last resort forshared/Microsoft.NETCore.App/<major>.*matching the major version the game build needs. If one matches, passDOTNET_ROOTin the child process's environment.EXECUTE_GAMEalready builds that environment as{ ...process.env, ...processEnv, ...plan.env }(gameHandlers.tsaround therealGameProcess().run(...)call), so the probe's result would slot in there rather than needing a new pathway. Log only a reason token for which root got used, never the full path, matching the redaction the launcher already does elsewhere in these logs.Never write to
/etc/dotnet/install_location*: this stays a per-launch, per-process fix, not a system-wide change made on the player's behalf. The per-Installation ENV variables field already lets a player setDOTNET_ROOTby hand today; this issue is about the launcher doing that automatically for the common case, not about adding a new way to configure it.Out of scope
Installing .NET for the player. This is about finding and pointing at a runtime that is already there, not about downloading or managing one.
Acceptance
EXECUTE_GAMEagainst a fake filesystem, covering the case where the probe finds a runtime andDOTNET_ROOTends up in the spawned environment.Reported on Discord by SatanicPanic (PikaOS); the Homebrew and immutable-distribution case by Valkyrien04 on this issue.