Commit Graph

3 Commits

Author SHA1 Message Date
enricobuehler c300deba9a feat(host/windows): Steam library auto-discovery on Windows
The Steam `LibraryProvider` keyed off `$HOME` + Linux paths, so the game
library was empty on Windows. Add Windows discovery: the default Steam
install dirs under Program Files (`ProgramFiles(x86)`/`ProgramFiles`/
`ProgramW6432`), with games on other drives picked up via each root's
`libraryfolders.vdf` — whose Windows values are backslash-escaped, so
unescape `\\` → `\`. The existing root-scan/dedup logic is shared via a
new `steam_roots_existing` helper. The custom store (mgmt JSON CRUD) was
already cross-platform; only Steam auto-discovery was Linux-only.

Not yet covered: a non-default Steam install dir (the registry
`Valve\Steam\InstallPath`). Degrades gracefully — no Steam → empty list.
clippy -D warnings + library tests green on Windows and Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 07:59:21 +00:00
enricobuehler ede393602c feat(launch): punktfunk/1 launch integration — client picks a title, host runs it
Plan step 4 (plumbing + host behavior). A client can ask the host to launch a
library title on connect; the host resolves it against ITS OWN library and runs it
in the session — the client sends only the store-qualified id, never a command, so a
remote peer can't inject one.

- Protocol (quic.rs): `Hello.launch: Option<String>` (the GameEntry id). Appended
  after `name`; when launch is present but name absent, a zero-length name placeholder
  keeps the offset deterministic — so a Hello with neither field stays byte-identical
  to the bitrate-era 26-byte form (test-asserted). Old peers ignore it; new hosts
  decode None from old clients. Round-trip + back-compat + truncation tests.
- Host: `library::launch_command(id)` resolves id → command via the host's own library —
  `steam_appid` → `steam steam://rungameid/<appid>` (appid validated as digits, the only
  client-influenced part), `command` → the host-stored command verbatim (trusted, never
  from the client). m3.rs sets PUNKTFUNK_GAMESCOPE_APP from it before bringup, exactly
  as the GameStream /launch path does (one session at a time). Unit-tested incl. an
  injection-attempt guard. Takes effect on the bare-spawn gamescope path; a no-op on a
  shared desktop / attach-to-existing session.
- C ABI: `punktfunk_connect_ex4` adds `launch_id` (NULL = none); `_ex3` now delegates to
  it. Threaded through NativeClient::connect → WorkerArgs → Hello.
- client-rs gains `--launch ID` (headless testing); client-linux passes None (no picker
  yet). Header regenerated.

Next: the Apple library grid passes the picked id via punktfunk_connect_ex4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 14:56:18 +00:00
enricobuehler 47f87a425a feat(host/library): game library API — Steam adapter + custom store
A new `library` module + four mgmt endpoints surface the host's games to clients
(plan: "surface the user's games"). An adapter layer (`LibraryProvider`) so future
stores (Heroic/Epic, GOG, Lutris) slot in behind one uniform `GameEntry`.

- SteamProvider: reads the LOCAL Steam install — no Steam Web API key, no network.
  Installed titles from steamapps/appmanifest_<appid>.acf; extra library folders
  (incl. paths with spaces) from libraryfolders.vdf; candidate roots cover classic,
  Flatpak and Deck layouts, canonicalized + deduped (the .steam/{steam,root}
  symlinks all fold to one). Runtimes/redistributables (Proton, Steam Linux Runtime,
  Steamworks Common, SteamVR) filtered out. Artwork = the public Steam CDN by appid
  (portrait/hero/logo/header), fetched directly by the client.
- Custom store: ~/.config/punktfunk/library.json, write-then-rename persisted,
  CRUD'd via the API — the "create custom entries via the admin web UI" requirement.
- API (under /api/v1, OpenAPI-documented + checked in): GET /library (all stores
  merged, sorted), POST /library/custom, PUT/DELETE /library/custom/{id}.
- `punktfunk-host library` subcommand dumps the resolved library as JSON (diagnostic,
  mirrors `openapi`).

Validated live against the real Steam library on the Bazzite box: 89 appmanifests →
78 games (11 tools filtered), correct titles/sort, and the CDN art URLs return 200.
5 unit tests for the VDF/ACF parsing, tool filter, art URLs, custom mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 13:43:03 +00:00