Skip to content

fix(watcher): find Steam EFT installs and recover from a missing logs path - #213

Open
JacobWoodson wants to merge 1 commit into
the-hideout:masterfrom
JacobWoodson:fix/issue-210-steam-logs-path
Open

JacobWoodson wants to merge 1 commit into
the-hideout:masterfrom
JacobWoodson:fix/issue-210-steam-logs-path

Conversation

@JacobWoodson

Copy link
Copy Markdown

Fixes #210.

Root cause is not what the issue describes

The issue attributes the failure to the discovery code relying on "a single machine-wide Steam uninstall registry key". The key and the registry view are actually fine — the value inside it is not.

On a machine that reproduces the bug (EFT installed through Steam, no Battlestate launcher):

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 3932890
  DisplayName     = [Escape from Tarkov]
  InstallLocation = []          <-- empty string, not absent
  UninstallString = ["C:\program files (x86)\steam\steam.exe" steam://uninstall/3932890]

GetDefaultLogsFolder guards with if (installPath == null), which an empty string passes. Discovery then builds relative paths — Path.Combine("", "Logs") is Logs, and Path.Combine("", "build", "Logs") is build\Logs — which get probed against TarkovMonitor's working directory rather than against the install. Both miss, the loop exhausts, and it throws No Tarkov install path found.

This is not an edge case. On that same machine, 57 of 98 Steam App * uninstall entries have no usable InstallLocation: some are written with the value blank (Destiny 2 shows the identical pattern), others are empty keys with no values at all. The uninstall entry alone is not a reliable source for a Steam install, which is why this PR adds the Steam library walk the issue asks for.

Worth recording for anyone who reads the issue first: the registry view is not the problem. Steam writes these entries to the 64-bit view (98 entries under Registry64, 0 under Registry32), so the existing non-WOW6432Node path was correct. This PR still checks both views, because HKLM\SOFTWARE\Valve\Steam genuinely is a 32-bit key.

The cascade after the throw

The reported second error is reproducible, and the ordering matters:

  1. The LogsPath getter catches the throw, reports getting logs path, and returns "".
  2. logFileCreateWatcher.Path = "" is a silent no-op — the setter short-circuits when the value equals the already-empty backing field, so nothing throws here.
  3. EnableRaisingEvents = true then throws FileNotFoundException: Error reading the directory., which is the second message users see.

Because the throw lands on step 3 rather than step 1, the remainder of Start() never runs, so processTimer and SetupScreenshotWatcher() are silently skipped too — the process and screenshot watchers die alongside log monitoring.

It also leaves EnableRaisingEvents false, and the LogsPath setter only re-points the watcher if (logFileCreateWatcher.EnableRaisingEvents). So after a failed startup, choosing a folder under Settings > Logs Folder writes the setting and does nothing else. Monitoring stays dead until the app is restarted, with no feedback that anything is still wrong.

Changes

All in GameWatcher.cs.

Discovery

  • IsNullOrEmpty guard on InstallLocation, so a blank value no longer produces relative path probes.
  • New Steam fallback: resolve the Steam root from Valve\Steam, enumerate libraries from libraryfolders.vdf, and read installdir from appmanifest_3932890.acf in each. Falls back to the conventional Escape from Tarkov folder name when the manifest is missing. This is what makes non-default libraries work.
  • Registry reads go through a helper that checks both the 32- and 64-bit views and swallows access errors.
  • GetDefaultLogsFolder returns "" instead of throwing. Both callers already handle an empty result; Settings.razor keeps its try/catch for genuine registry failures.

Recoverable startup

  • Start() starts the process timer and screenshot watcher first, then calls StartLogWatcher(). A missing folder reports one actionable message (Could not find the Escape from Tarkov logs folder. Choose it under Settings > Logs Folder to start monitoring.) and leaves the rest of the app running. No empty path reaches FileSystemWatcher.

Deferred startup completion

  • Explicit watcherStarted / logWatcherStarted flags replace the EnableRaisingEvents check. Setting LogsPath after a deferred start now completes startup, so the existing customLogsPath handler in MainBlazorUI recovers without a restart. MainBlazorUI itself is unchanged.
  • GetLatestLogFolder called logFolders.Last() on a possibly-empty array; it now returns "" for a logs folder with no session subfolders, and the caller lets the create-watcher pick up the next one EFT writes.

Verification

Built against the pinned SDK (10.0.302): 0 errors, 230 warnings — identical to the master baseline.

Before/after on the reproducing machine, invoking the compiled GetDefaultLogsFolder and then replaying what Start() does with the result:

##### BEFORE (master) #####
THREW: Exception: No Tarkov install path found

##### AFTER (this branch) #####
RESULT: 'G:\SteamLibrary\steamapps\common\Escape from Tarkov\build\Logs'
WATCHER: started OK

That install is in a non-default Steam library with the build\Logs layout and no Battlestate registry key. Also exercised directly: library enumeration returns all four libraries from libraryfolders.vdf; manifest parsing returns the expected installdir; a missing manifest and a nonexistent install path both return empty without throwing; and both the Logs and build\Logs layouts resolve.

Existing Battlestate installs are unaffected — that registry branch is unchanged apart from the stricter empty-value guard, and it is still tried before any Steam lookup.

Not addressed

  • The user.config / missing customLogsPath node item from the issue. That concerns hand-editing and external scripts, not app behavior; the app reads the setting through the generated wrapper, which returns the default when the node is absent.
  • Tests. There is no test project in the solution, and GetDefaultLogsFolder reaches the registry and filesystem through static calls, so making it testable needs seams that felt out of scope for a bugfix. Happy to do it as a follow-up if you want it.
  • Deferring watcher startup until the shell is shown, which the issue also proposes. I could not reproduce the frozen-navigation symptom from this failure — the two messages involved are short, and MessageLog buffers into a list so constructor-time messages still render once the WebView loads. That symptom looks like Handle PvpSeason session mode without flooding or freezing the Dashboard #187 (an oversized message breaking the layout) rather than this bug, so I left eft.Start() where it is.

🤖 Generated with Claude Code

… path

Steam writes its uninstall entry for EFT with an empty InstallLocation, so
GetDefaultLogsFolder fell through every candidate and threw. The null-only
guard also let the empty value through, producing relative "Logs" and
"build\Logs" probes against the process working directory.

Discovery now falls back to walking the Steam libraries from
libraryfolders.vdf and reading appmanifest_3932890.acf, which resolves
installs in non-default libraries. All registry lookups check both the 32-
and 64-bit views, since Valve's own keys are 32-bit.

A missing logs folder is no longer fatal: GetDefaultLogsFolder returns an
empty string, and Start() keeps the process and screenshot watchers running
while reporting one actionable message. Previously the empty path reached
FileSystemWatcher, which threw "Error reading the  directory." from
EnableRaisingEvents and skipped the rest of Start().

Because that throw left EnableRaisingEvents false, the LogsPath setter's
recovery branch never ran and choosing a folder in Settings silently did
nothing until restart. Startup state is now tracked explicitly so setting a
path completes the deferred startup.

Fixes the-hideout#210

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@GiribaldiTTV

Copy link
Copy Markdown
Contributor

Fix was inncluded in PR211. Please verify resolution before closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Steam installs can leave GameWatcher without a usable EFT logs path

2 participants