Skip to content

Fix #675: cross-platform settings path + Windows --cached fixes - #677

Open
amillionbouncyballs wants to merge 5 commits into
e-dream-ai:masterfrom
amillionbouncyballs:675-cross-platform-settings-path
Open

Fix #675: cross-platform settings path + Windows --cached fixes#677
amillionbouncyballs wants to merge 5 commits into
e-dream-ai:masterfrom
amillionbouncyballs:675-cross-platform-settings-path

Conversation

@amillionbouncyballs

Copy link
Copy Markdown
Collaborator

Fixes #675: shared code was hardcoding ~/.config/infinidream/settings.json regardless of platform, so the first-run warning reported the wrong settings path on Windows and Mac. g_Settings()->ConfigPath() is now used so the reported path always matches where the client actually wrote the file. Also adds and fixes --cached (offline, local-cache-only playback) support on Windows.

What changed and why

a7091a8 / 1108058 (original fix) — report the real settings path on all platforms; parse --cached in WinMain via CommandLineToArgvW and honor it in Startup(), forcing offline mode before the empty-cache check runs (not after), so an empty-cache --cached run doesn't fall through into starting real auth during teardown.

Found during manual Windows QA of the above, fixed in this branch:

  • client_win32.h has its own legacy screensaver-flag parser (/s, /p, /c, /a, /t, /x, /r) that re-parses the raw command line independently of the --cached parsing in main(). It only strips one leading dash, so --cached left it pointing at the second dash, matched no recognized flag, and left m_ScrMode at the invalid eNone — which aborted Startup() entirely before the --cached logic ever ran. Unrecognized flags now fall back to windowed mode instead of aborting.
  • Even with that fixed, an empty-cache --cached run still briefly created and showed a window, because client_win32.h's Startup() builds the display before calling into the shared Startup() that contains the cache check. Extracted that check into an idempotent CheckCachedOnlyMode() and call it on Windows before any window is created, so an empty cache now exits with no window at all. Mac/Linux are unaffected — they still reach the same check from the shared Startup() as before.
  • With those fixed, --cached playback showed permanently red "Busy" and "Remote" HUD indicators, because both legitimately reuse m_MultipleInstancesMode / skip the WebSocket connection to get offline behavior — which is indistinguishable, to those indicators, from "another instance is running" or "connection lost". Suppressed both specifically for --cached mode, mirroring the existing m_OfflineDueToNoInternetOnly guard, with comments explaining why.

Testing

Manually tested end-to-end on both Windows and Linux:

  • Fresh first run: settings path in the first-run log message matches the platform's real config path; sign-in flow appears as expected.
  • --cached with an empty cache: no window opens, process exits cleanly with a clear log message, no auth thread starts.
  • --cached with a populated cache (after signing in and letting content download): plays offline from cache, no sign-in wizard, no "Busy"/"Remote" indicators.

amillionbouncyballs and others added 3 commits August 11, 2026 12:02
… Windows

The first-run auth warning hardcoded ~/.config/infinidream/settings.json and
suggested --cached. Neither holds outside Linux: the path is wrong on Windows
and macOS, and --cached was never parsed on Windows at all.

Settings path
-------------
JSONStorage::Initialise() already resolves the real per-platform location into
m_ConfigPath, built from each client_*.h's m_AppData, but never exposed it.
Add ConfigPath() to IStorageInterface / JSONStorage / CSettings and use it at
all three message sites in EDreamClient.cpp -- the issue named one; the two in
LoginWithMagicLinkCode() hardcoded the same literal. The messages now name the
file the client actually opened, so they cannot drift from reality again.

--cached on Windows
-------------------
Parsing alone would not have been enough: m_CachedOnlyMode was consumed only
inside the #ifdef LINUX_GNU block in client.h, so the flag would have been
accepted and then silently inert -- the same class of bug. So:

- WinMain parses --cached via CommandLineToArgvW. lpCmdLine is ANSI and
  untokenised; this gives argv semantics with the CRT's own quoting rules.
- The cached-only branch moves out of the platform guard. Only the genuinely
  Linux console magic-link flow stays inside it.
- The no-cache failure goes through g_Log->Error(), which already forwards to
  PlatformUtils::NotifyError() (CLog::Error, Common/Log.cpp). On Windows the
  client is a GUI binary with no console attached, so stderr goes nowhere and
  the log file is the only channel that reaches a user.

Preventing recurrence
---------------------
This bug was not caused by an #ifdef -- it was caused by the absence of one.
Shared code stated a platform fact as a string literal, which compiles cleanly
on all three platforms and is wrong on two of them, with no build-time signal.

- PlatformUtils.h loses its #ifdef WIN32 API block. Win32SetMessageWindow
  becomes SetNativeMessageWindow, declared unconditionally, with explicit
  commented no-ops on Linux and Mac. Unconditional declarations preserve the
  property that matters: a missing implementation is a link error on that
  platform, whereas a missing #ifdef branch fails silently at runtime.
- scripts/check_platform_paths.py fails on platform path literals in shared
  code, allowlisting the three client_*.h shims whose job is to define them.
  Wired into a new .github/workflows/lint.yml. Verified to flag the original
  line when reintroduced.
- AGENTS.md documents the convention, the three-implementation table, and why
  the link error beats an #ifdef. Also corrects two stale references to
  e-dream.xcodeproj; the file is infinidream.xcodeproj.

Verified on Linux: builds clean, and under a throwaway HOME the warning follows
it, which proves the path is resolved rather than literal.

Windows and macOS are unbuilt here. The WinMain parsing, the
PlatformUtils_win.cpp rename and the Mac no-ops need a build on those platforms.
No Mac hardware available, so the Mac path is compile-unverified.

Windows test
------------
Build Release | x64 from client_generic/MSVC/e-dream.sln, or:

    cd client_generic\WinBuild
    python build.py

Binary lands in client_generic\MSVC\Release\infinidream.exe.

WARNING: step 1 deletes the data folder, including cached videos. Back it up
first if you want to keep them, or expect a re-download.

    :: 1. Settings path. Clean install, run, then read the log.
    rmdir /s /q "%LOCALAPPDATA%\Infinidream"
    client_generic\MSVC\Release\infinidream.exe
    powershell -Command "Select-String 'settings.generator.nickname' $env:LOCALAPPDATA\Infinidream\Logs\*.log"

    :: expect: ...in C:\Users\<you>\AppData\Local\Infinidream\settings.json
    :: bug:    ...in ~/.config/infinidream/settings.json

    :: 2. --cached with an empty cache. Should exit without opening a window.
    client_generic\MSVC\Release\infinidream.exe --cached
    powershell -Command "Select-String 'cached videos' $env:LOCALAPPDATA\Infinidream\Logs\*.log"

    :: expect: --cached requested but no cached videos found.
    :: bug:    a window opens, or the flag is ignored entirely

    :: 3. --cached with content. Sign in, let some dreams download, quit, then:
    client_generic\MSVC\Release\infinidream.exe --cached

    :: expect: plays offline from cache with no sign-in prompt

To watch the log live during any of the above:

    powershell -Command "Get-Content -Wait -Tail 20 $env:LOCALAPPDATA\Infinidream\Logs\*.log"

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The --cached branch assigned m_MultipleInstancesMode last, after the empty-cache
check had already returned false. That flag gates EDreamClient::InitializeClient(),
which is what keeps the auth thread -- and therefore the sign-in wizard -- out of
an explicitly offline session. Assigning it last meant the failure path never set
it at all, so a --cached run with an empty cache still started auth during
teardown:

    [error]: --cached requested but no cached videos found. Run without --cached
             first to download content.
    CElectricSheep::Shutdown()
    [info]: Starting Authentication...
    [warning]: No sealed session or API key found. Set settings.generator.nickname
               in <path> and run interactively, or run with --cached to play
               cached videos.

That last line tells the user to run with --cached, which is precisely what they
had just done. Harmless, since the app is already exiting by then, but confusing
to anyone reading the log after a failed --cached run.

Hoisting the assignment to the top of the branch skips auth on both paths. The
same run now ends:

    [error]: --cached requested but no cached videos found. Run without --cached
             first to download content.
    [info]: Disabling auth in multiple instance mode

No behaviour change on the --cached success path, and none to startup without
--cached, where the sign-in wizard still appears exactly as it did before on all
three platforms. Suppressing the wizard is specific to --cached and predates this
branch: it is the point of the flag, which is a user asking to play what is on
disk without authenticating.

Verified on Linux under a throwaway HOME. Windows and macOS remain unbuilt here;
this change is in shared code with no platform-conditional paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Manual Windows QA of a7091a8/11080580 turned up two --cached bugs and
a UX side effect that the fixes above didn't cover:

- client_win32.h's own legacy screensaver flag parser (/s, /p, /c, /a,
  /t, /x, /r) re-parses the raw command line independently of main()'s
  --cached handling. It only strips one leading dash, so "--cached"
  left it pointing at the second dash, matched none of the known
  flags, and left m_ScrMode at the invalid eNone. That fails the mode
  check right after and aborts Startup() before
  CElectricSheep::Startup() -- where the --cached/cache-check logic
  actually lives -- ever runs. Unrecognized flags now fall back to
  eWindowed instead of aborting.

- Even with that fixed, an empty-cache --cached run still briefly
  created and showed a window: win32's Startup() builds the display
  before calling the shared Startup() that contains the cache check.
  Extracted that check into CheckCachedOnlyMode() (idempotent, guarded
  by m_CachedOnlyModeChecked) and call it from win32's Startup() right
  after command-line/logging setup, before any display is created, so
  an empty cache now exits with no window at all. Mac/Linux still hit
  the same check from the shared Startup() as before.

- With those fixed, --cached playback showed permanently red "Busy"
  and "Remote" HUD indicators. Both flags legitimately reuse
  m_MultipleInstancesMode/skip the WebSocket to get offline behavior,
  which is indistinguishable, to those indicators, from "another
  instance is running" and "lost connection" -- so they lit up for
  expected, working offline playback. Suppressed both specifically for
  m_CachedOnlyMode, mirroring the existing m_OfflineDueToNoInternetOnly
  guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
amillionbouncyballs and others added 2 commits August 11, 2026 16:58
CElectricSheep_Linux::Startup() calls AddDisplay() before the shared
CElectricSheep::Startup() where the --cached cache check lives, so a
--cached run with an empty cache created a Vulkan window and tore it
down again milliseconds later when the check failed. Windows already
avoided that by calling CheckCachedOnlyMode() ahead of its own display
creation; do the same on Linux, straight after InitStorage()/AttachLog()
so the failure still reaches the log.

This is what the m_CachedOnlyModeChecked guard is for: the re-check in
the shared Startup() is now a no-op on Linux as well as Windows. Mac is
unchanged - it still reaches the check for the first time in the shared
path. Comments in client.h updated to match.

Verified on Linux (Arch, Wayland) with the four steps already confirmed
on Windows:

1. --cached, empty cache (fresh HOME): exits with no window created at
   all - zero Vulkan/Wayland init lines - reporting "--cached requested
   but no cached videos found" to both the log and stderr via
   NotifyError().
2. --cached, populated 45.7 GB cache: plays offline, logging "cycling
   through 795 (45.7 GB) of cached videos" before the window opens, then
   "Disabling auth in multiple instance mode". No "Starting
   Authentication..." and no sign-in wizard.
3. Fresh state without --cached: the e-dream-ai#675 message reports the real
   resolved settings path (it followed an overridden HOME) rather than a
   hardcoded ~/.config/infinidream/settings.json, and the GUI sign-in
   wizard still appears.
4. Normal online run against real settings: authenticates, saves a
   sealed session, connects the WebSocket and plays; settings.json
   unchanged and still valid.

scripts/check_platform_paths.py passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
build_appimage.py is on master, so anyone who runs the Linux AppImage
build produces client_generic/LinuxBuild/__pycache__/ and then carries it
as an untracked directory on every branch they check out. The root
.gitignore already carries client_generic/WinBuild/__pycache__/ for the
same reason on the Windows side; this mirrors it for Linux.

Unrelated to the settings-path and --cached work on this branch, but it
belongs on master and there is no other open PR to carry it.

The sibling deps-cache/ and model-cache/ directories are deliberately not
here: they are fetched by CMakeLists.txt on the rife branches, whose
.gitignore already covers them, and nothing on master generates them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

First-run auth message shows Linux settings path and suggests --cached, neither valid on Windows

2 participants