diff --git a/launcher/Backend/LocalOverrides.cs b/launcher/Backend/LocalOverrides.cs index b627557..6775714 100644 --- a/launcher/Backend/LocalOverrides.cs +++ b/launcher/Backend/LocalOverrides.cs @@ -26,14 +26,21 @@ public static class LocalOverrides public const string ManifestEnvVar = "DORKNET_LOCAL_MANIFEST"; public const string ReleasesEnvVar = "DORKNET_LOCAL_RELEASES"; - /// Returns the configured manifest path if the env var is - /// set AND the file exists; null otherwise. Caller decides whether - /// to fall back to the network. + /// Returns a local manifest path to use instead of the + /// GitHub fetch. Prefers DORKNET_LOCAL_MANIFEST when set; if + /// that's unset, falls back to a side-by-side versions.json + /// next to dorknet.exe (so dev checkouts against a private + /// repo work without env-var wiring — the launcher's csproj copies + /// the repo-root versions.json to its bin output on build). public static string? GetLocalManifestPath() { var path = Environment.GetEnvironmentVariable(ManifestEnvVar); - if (string.IsNullOrEmpty(path)) return null; - return File.Exists(path) ? path : null; + if (!string.IsNullOrEmpty(path) && File.Exists(path)) return path; + + var sideBySide = Path.Combine(AppContext.BaseDirectory, "versions.json"); + if (File.Exists(sideBySide)) return sideBySide; + + return null; } /// Returns the configured releases-root if the env var is @@ -87,4 +94,23 @@ public static class LocalOverrides ? $"{envVar} -> {raw} [active]" : $"{envVar} -> {raw} [SET BUT NOT FOUND on disk]"); } + + /// Best-effort description of where the manifest is + /// actually coming from this session. Mainly for the Settings UI + /// so the user can tell at a glance "side-by-side dev manifest" + /// vs "live GitHub fetch" vs "broken env-var path". + public static string DescribeManifestSource() + { + var env = Environment.GetEnvironmentVariable(ManifestEnvVar); + if (!string.IsNullOrEmpty(env)) + { + return File.Exists(env) + ? $"manifest source: {env} (env var)" + : $"manifest source: {env} (env var, NOT FOUND — falling back)"; + } + var sideBySide = Path.Combine(AppContext.BaseDirectory, "versions.json"); + if (File.Exists(sideBySide)) + return $"manifest source: {sideBySide} (side-by-side fallback)"; + return "manifest source: live GitHub fetch (no local override)"; + } } diff --git a/launcher/DorkNet.Launcher.csproj b/launcher/DorkNet.Launcher.csproj index 2eaf223..5b09e99 100644 --- a/launcher/DorkNet.Launcher.csproj +++ b/launcher/DorkNet.Launcher.csproj @@ -27,4 +27,17 @@ + + + + versions.json + PreserveNewest + + + diff --git a/launcher/MainWindow.xaml.cs b/launcher/MainWindow.xaml.cs index 9b245c2..b081528 100644 --- a/launcher/MainWindow.xaml.cs +++ b/launcher/MainWindow.xaml.cs @@ -184,13 +184,25 @@ public partial class MainWindow : Window private async Task RefreshManifestAsync() { _manifest = await new VersionsManifest().FetchAsync(); + VersionSelect.Items.Clear(); if (_manifest is null) { - HostStatusText.Text = "Couldn't load versions.json from GitHub. " + - "Check internet, then reopen."; + // Empty placeholder so the user sees *something* in the + // dropdown instead of a silent blank — helps when the public + // repo is unreachable (e.g. private repo + no env override). + var placeholder = new ComboBoxItem + { + Content = "(no versions — check internet or set DORKNET_LOCAL_MANIFEST)", + IsEnabled = false, + Tag = null, + }; + VersionSelect.Items.Add(placeholder); + placeholder.IsSelected = true; + HostStatusText.Text = "Couldn't load versions.json. " + + "Either the repo isn't reachable, or DORKNET_LOCAL_MANIFEST " + + "points at a missing/invalid file — check Settings."; return; } - VersionSelect.Items.Clear(); foreach (var v in _manifest.Branches.Where(b => b.Supported)) { var item = new ComboBoxItem