Skip to content

Commit 895fa2b

Browse files
fix(magos-modificus): search all Steam libraries for the compatdata prefix (#21)
## What Small follow-up fix to the Phase 1 Steam discovery library (#18): the compatdata (Proton prefix) resolution now searches **all Steam libraries**, not just the main install. ## Why A launch-smoke-test on a real Linux machine surfaced this: the user's Darktide compatdata lives at `/games/steamapps/compatdata/1361210/` — under a **Steam library drive**, not the main Steam install. Steam places the prefix on whichever drive it chose at install time, which is frequently a library. The previous logic checked only `<SteamInstallPath>/steamapps/compatdata/<appid>/`, so it reported `CompatdataPath: (missing)` → `DiscoveryIncomplete`, blocking the launch. ## The fix `SteamService.FindCompatdata` now probes candidates across **the main install + each library** (main install first for prior-behavior preservation, then libraries in `libraryfolders.vdf` order, deduped), first existing wins. The discovery already parsed `libraryfolders.vdf` for the Darktide search — reuses that list. `DiscoveryResult.CompatdataPath` stays a single string — **no interface change**. Windows discovery + Proton/Darktide resolution untouched. ## Test New `Compatdata_in_secondary_library_is_found` — fixtures a secondary library with the compatdata (not under the main install) → asserts `Status: Complete` + the correct library path. Existing main-install-compatdata test still passes (main install probed first). ## Verification - **coder** — impl + test; `dotnet build` 0/0; `dotnet test` green (Steam 39 = 38 + 1; full main suite 117). No new deps. - **Light review path** — this is a one-method targeted fix with a clear test; skipping the formal qa/code-review cycle. CI gates Win + Linux on this PR; the diff is small for direct review. - After merge: re-run `dotnet run --project tests/Magos.Modificus.EnginseerClient.Tests -- discover` → `Status: Complete` (compatdata found under the library). Builds on merged #18 (Steam). Independent of #20 (Enginseer-client, still open).
1 parent 781d65c commit 895fa2b

2 files changed

Lines changed: 63 additions & 5 deletions

File tree

magos-modificus/steam/SteamService.cs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ private DiscoveryResult DiscoverLinux()
6969

7070
var libraries = ReadLibraries(resolved.Path, warnings);
7171
var darktide = FindDarktide(libraries, warnings);
72-
var compatdata = FindCompatdata(resolved.Path);
72+
var compatdata = FindCompatdata(resolved.Path, libraries);
7373
var proton = FindProton(resolved.Path, _options.LinuxCompatibilityToolsDir, warnings);
7474

7575
var status = StatusForLinux(resolved.Path, darktide, compatdata, proton?.Path);
@@ -202,12 +202,48 @@ private IReadOnlyList<string> ReadLibraries(string steamRoot, List<string> warni
202202
return null;
203203
}
204204

205-
private string? FindCompatdata(string steamRoot)
205+
/// <summary>
206+
/// Resolves the Darktide compatdata (Proton prefix) for the configured app
207+
/// id. Probes the main Steam install first, then each library declared in
208+
/// <c>libraryfolders.vdf</c> (in order); the first existing dir wins.
209+
/// </summary>
210+
/// <remarks>
211+
/// The prefix is created on whichever drive Steam chose at install time, so
212+
/// it frequently lives under a Steam *library* rather than the main install
213+
/// (e.g. <c>/games/steamapps/compatdata/&lt;appid&gt;/</c>). Probing the
214+
/// main install first preserves prior behavior — when the prefix is there it
215+
/// still wins — and the library scan is deterministic (VDF order).
216+
/// </remarks>
217+
private string? FindCompatdata(string steamRoot, IReadOnlyList<string> libraries)
206218
{
207-
var dir = Path.Combine(
208-
steamRoot, "steamapps", "compatdata", _options.DarktideAppId.ToString(CultureInfo.InvariantCulture));
219+
var appId = _options.DarktideAppId.ToString(CultureInfo.InvariantCulture);
220+
221+
// Main install first, then each library in VDF order — the main install
222+
// is yielded explicitly so it's probed first even if the VDF lists it
223+
// later (or omits it); the explicit duplicate is skipped below.
224+
foreach (var root in CompatdataCandidateRoots(steamRoot, libraries))
225+
{
226+
var dir = Path.Combine(root, "steamapps", "compatdata", appId);
227+
if (Directory.Exists(dir))
228+
{
229+
return dir;
230+
}
231+
}
232+
233+
return null;
234+
}
209235

210-
return Directory.Exists(dir) ? dir : null;
236+
private static IEnumerable<string> CompatdataCandidateRoots(string steamRoot, IReadOnlyList<string> libraries)
237+
{
238+
yield return steamRoot;
239+
foreach (var lib in libraries)
240+
{
241+
// Skip the main install when the VDF lists it — it's yielded first above.
242+
if (!string.Equals(lib, steamRoot, StringComparison.Ordinal))
243+
{
244+
yield return lib;
245+
}
246+
}
211247
}
212248

213249
/// <summary>

magos-modificus/tests/Magos.Modificus.Steam.Tests/LinuxDiscoveryTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,28 @@ public void Darktide_in_secondary_library_is_found()
115115
Assert.Contains(result.Warnings, w => w.Contains("Searched 2 Steam libraries", StringComparison.Ordinal));
116116
}
117117

118+
[Fact]
119+
public void Compatdata_in_secondary_library_is_found()
120+
{
121+
// The Proton prefix (compatdata) is created on whichever drive Steam
122+
// chose at install time — frequently a Steam *library* drive rather than
123+
// the main install (e.g. /games/steamapps/compatdata/<appid>/). Discovery
124+
// must probe each library, not just the main install, or it reports
125+
// CompatdataPath missing → DiscoveryIncomplete and blocks the launch.
126+
using var fx = new SteamFixture();
127+
var secondary = Path.Combine(fx.TempRoot, "secondary-lib");
128+
Directory.CreateDirectory(secondary);
129+
fx.WithLibraryFoldersAtSteamRoot(fx.SteamRoot, secondary);
130+
fx.WithDarktide(fx.SteamRoot);
131+
fx.WithCompatdata(secondary); // prefix only under the secondary library
132+
fx.WithProtonInCommon(fx.SteamRoot, "Proton - Experimental");
133+
134+
var result = fx.Service.Discover();
135+
136+
Assert.Equal(DiscoveryStatus.Complete, result.Status);
137+
Assert.Equal(fx.ExpectedCompatdataPath(secondary), result.CompatdataPath);
138+
}
139+
118140
[Fact]
119141
public void Missing_steam_root_falls_back_to_flatpak_when_valid()
120142
{

0 commit comments

Comments
 (0)