Skip to content

feat: build GameHours desktop foundation - #1

Draft
Ayerdi wants to merge 795 commits into
mainfrom
feat/desktop-foundation
Draft

feat: build GameHours desktop foundation#1
Ayerdi wants to merge 795 commits into
mainfrom
feat/desktop-foundation

Conversation

@Ayerdi

@Ayerdi Ayerdi commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Summary

This draft builds the Windows desktop foundation for GameHours as a local-first, launcher-independent playtime tracker.

  • durable measured play sessions with checkpoint/crash recovery and conservative suspend/resume protection
  • Steam/Epic/GOG discovery plus layered executable resolution and manual decisions in Pendientes
  • local achievements, including Steam/GSE-compatible local sources, and historical SRUM evidence
  • Library, Activity, Calendar, Statistics, Settings and session-detail desktop UX
  • focused time and estimated-active telemetry without changing authoritative executed time
  • safe SQLite backup/restore and portable JSON import/export
  • event-driven process/achievement observation with conservative reconciliation fallbacks
  • Velopack packaging/self-update with a typed GitHub Releases source
  • per-user single-instance protection before SQLite/tracking startup
  • public-release pipeline prepared for Authenticode signing without making an external signing provider a current product-development blocker

Current engineering state

The broad foundation has extensive automated and real-Windows evidence. Suspend/resume remains protected by deterministic tests but is intentionally not claimed as real-machine verified, and the new GSE catalogue-provisioning lifecycle still requires its final real-game validation.

The installed Velopack path is verified on real Windows. 0.2.0-beta.1 was installed over the existing application, detected 0.2.0-beta.2 with one delta, downloaded from the in-app UI while a game was running, shut GameHours down cleanly, applied the update, restarted automatically, re-detected the still-running game and preserved the pre-update session/data. The newly tracked game was not duplicated. Exact evidence is in docs/INSTALLED-UPDATE-VALIDATION-2026-08-29.md.

The expanded runtime performance/memory gate is also verified on real Windows. Equal 30-second samples at idle and with a tracked game running remained effectively flat: 0.04% vs 0.07% average CPU, 157.1 vs 156.9 MiB average private memory, 182.8 vs 183.0 MiB average Working Set, 0.01% GC pause in both states, no Gen2 collections, identical 26.0 MiB committed peak and 0.5 MiB fragmentation peak. There is no evidence justifying speculative GC or memory tuning. Exact evidence is in docs/RUNTIME-PERFORMANCE-VALIDATION-2026-08-29.md.

The single-instance behavior is now also verified on real Windows: launching GameHours again while the first instance is running does not start a second tracker/application instance.

Automated validation

Normal PR CI runs locked restore, Release build, the full test suite and a self-contained Desktop publish. On pull_request GitHub checks out its generated merge ref, so evidence below distinguishes the branch HEAD from the merge commit actually executed.

Latest evidence:

  • branch HEAD: 3bf320f9cce4605bd5eeed4280bdbe390780e080;
  • CI #785 (33274156870): success on generated merge ref e53c14afb82ca7c85d983c03723a2ba72023c77f (3bf320f9... merged with current main);
  • Windows Server 2025 / .NET SDK 8.0.424;
  • locked restore ✅;
  • Release build ✅ — 0 warnings / 0 errors;
  • GameHours.Tests ✅ — 130/130;
  • GameHours.Windows.Tests ✅ — 166/166;
  • total discovered/passed: 296/296;
  • self-contained win-x64 Desktop publish ✅;
  • Velopack package/install smoke skipped as intended while the PR remains draft.

CI #782 is intentionally not hidden: it rejected the first explicit GSE-confirmation UX because MessageBox was ambiguous between WPF and WinForms. The implementation was corrected to use System.Windows.MessageBox, and subsequent CI is green.

Earlier relevant evidence includes CI #636 for modern portable GSE runtime-state support, #725/#729 for the Velopack two-version/update-chain path, #736 for install/uninstall data survival and #751 for single-instance/probe regression coverage.

GSE/Goldberg achievement provisioning

The original real-machine diagnosis of Click the Button (Steam AppID 3946950) found GSE configuration but no steam_settings\achievements.json catalogue and no persisted user achievement state. Broader scanning correctly could not recover data that did not exist.

Follow-up research of current GSE/Goldberg and Hydra showed the missing lifecycle step: GSE resolves achievement unlocks against its settings catalogue. GameHours now has a generic, bounded provisioning path for this case, documented in docs/GSE-ACHIEVEMENT-PROVISIONING-2026-08-29.md.

The implementation:

  • centralizes bounded steam_settings discovery and covers flat/common Unreal/Unity/Steamworks layouts plus coldclient/steam_settings;
  • never intentionally broadens into sibling games and skips reparse-point traversal;
  • shares one conservative GSE/Goldberg detector across diagnostics and provisioning;
  • obtains only public achievement API names from Valve's keyless global-achievement endpoint;
  • creates a minimal GSE-compatible catalogue with name, displayName, empty description and "hidden": "0";
  • does not download icons into game installations;
  • never overwrites an existing catalogue, never creates an empty one, and never writes or infers user unlock state;
  • writes via a unique temporary file followed by move-into-place;
  • keeps the normal achievement provider/monitor local-only.

Crucially, opening a game detail is read-only. When GSE is detected without catalogue/state, the UI explains the situation. Provisioning occurs only after the user clicks Actualizar logros and explicitly accepts a WPF confirmation that names the file being created and explains the historical limitation. Cancelling performs no provisioning write.

This can enable GSE/Goldberg to record future unlocks. It does not reconstruct achievements that the emulator never persisted. The real Click installation and Big Walk remain the final manual validation cases; Big Walk is not claimed fixed until its actual local layout is observed.

Single-instance protection

GameHours takes a per-user process lease under its local data directory after Velopack lifecycle hooks but before WPF/SQLite/tracking initialization. A second Desktop launch cannot start a second tracker against the same local database and exits with a clear user-facing message. The lease is released by process/file-handle lifetime rather than a persisted flag.

Automated coverage is green and the behavior has been confirmed on real Windows.

Next real-Windows check

The next focused gate is the GSE lifecycle:

  1. restore Click the Button to the original missing-catalogue condition (the earlier manual experiment created a catalogue that was not in the repack);
  2. confirm merely opening its detail creates nothing;
  3. click Actualizar logros, cancel once and verify no file is created;
  4. accept on the next attempt and verify the catalogue appears only inside the target game;
  5. restart the game so GSE loads the definitions, trigger a fresh achievement and confirm runtime state + GameHours observation;
  6. repeat against Big Walk if it is recognized as GSE/Goldberg; otherwise use the existing read-only probe to characterize its source before adding any support.

Do not expect historical unlocks that were never written by the emulator to appear automatically.

Distribution state

GitHub Releases remains the selected initial beta/stable update origin. Signing is still desirable before broad public distribution, but it is not a blocker for current product development.

Research against comparable open-source desktop/game projects showed that mature OSS projects such as Playnite and Heroic use SignPath Foundation rather than requiring the project to own a commercial Azure/PKI stack. GameHours therefore defers provider provisioning until it is useful for real distribution. docs/CODE-SIGNING-PROVIDER-DECISION.md records the provider requirements and current alternatives.

The existing package/release hardening stays in place: package-content validation, immutable release versions, SHA-256 manifests, GitHub attestations, updater/recovery checks and public upload as the final side effect.

Later distribution gate

Before broad public distribution, select a publicly trusted Authenticode route appropriate for the actual publisher identity, integrate it at Velopack's signing boundary, then verify a signed install/update/recovery cycle and SmartScreen behavior. This is deliberately deferred rather than used to block current GameHours product work.

@Ayerdi
Ayerdi force-pushed the feat/desktop-foundation branch from 00ae3da to 651d203 Compare August 23, 2026 10:06
Ayerdi added 30 commits August 29, 2026 20:11
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.

1 participant