Skip to content

feat(update-checker): add in-game update notification via GitHub Releases API#98

Merged
fbraz3 merged 8 commits into
mainfrom
feature/update-checker
Apr 22, 2026
Merged

feat(update-checker): add in-game update notification via GitHub Releases API#98
fbraz3 merged 8 commits into
mainfrom
feature/update-checker

Conversation

@fbraz3

@fbraz3 fbraz3 commented Apr 21, 2026

Copy link
Copy Markdown
Owner

Summary

Implements a background update checker for both GeneralsX and GeneralsXZH, using libcurl + SDL thread. On tagged release builds, the game checks the latest release from the GitHub API in the background and shows a notification button at the bottom-right corner of the main menu if a newer version is available.

Closes #51

What Changed

New files

  • cmake/curl.cmake — libcurl dependency (vcpkg, sectransp SSL on macOS)
  • Generals/Code/GameEngine/Include/Common/UpdateChecker.h
  • Generals/Code/GameEngine/Source/Common/UpdateChecker.cpp
  • GeneralsMD/Code/GameEngine/Include/Common/UpdateChecker.h
  • GeneralsMD/Code/GameEngine/Source/Common/UpdateChecker.cpp

Updated files

  • vcpkg.json — added libcurl 8.13.0 dependency
  • CMakePresets.json + cmake/config-build.cmakeSAGE_UPDATE_CHECK flag (auto-ON with SAGE_USE_SDL3)
  • Generals/Code/GameEngine/CMakeLists.txt + GeneralsMD/Code/GameEngine/CMakeLists.txt
  • Generals/Code/GameEngineDevice/CMakeLists.txt + GeneralsMD/Code/GameEngineDevice/CMakeLists.txt
  • Generals/Code/GameEngine/Include/Common/GlobalData.h + .cppm_checkForUpdates field
  • GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h + .cpp — same
  • Both MainMenu.cpp files — dynamic button creation + click handler
  • flatpak/com.fbraz3.GeneralsX.yml + com.fbraz3.GeneralsXZH.yml — curl module added
  • CLAUDE.mdAGENTS.md rename for AI agent compatibility
  • .vscode/extensions.json — extension recommendations updated

Implementation Details

UpdateChecker

  • Background check runs in SDL_CreateThread thread to avoid blocking game startup
  • Uses SDL_AtomicInt for thread-safe result communication
  • Only runs on tagged builds (GitTag != "" and GitUncommittedChanges == false)
  • GENERALS_FORCE_UPDATE_CHECK env var bypasses tag check for testing

UI

  • Replaced hardcoded ButtonGetUpdate (WND position 622,32 at 800x600 — behind logo at modern resolutions) with a dynamically created button via gogoGadgetPushButton
  • Position: TheDisplay->getWidth() - 260 - 8, getHeight() - 26 - 8 (bottom-right, resolution-aware)
  • Font: winFindFont("Arial", 10, FALSE) — replaces oversized WND default
  • Button text: "Update available: vX.X"
  • Tooltip: "Update available: vX.X - click to download"
  • Click: SDL_OpenURL("https://github.com/fbraz3/GeneralsX/releases")

Testing

Tested on macOS (ARM64, macos-vulkan preset) with:

GENERALS_FORCE_UPDATE_CHECK=1 ./scripts/build/macos/run-macos-zh.sh -win

Button appears at bottom-right ~8s after game launch with correct text, font, and click behavior.

fbraz3 added 7 commits April 21, 2026 13:52
…ases API

Implements a background update checker for both GeneralsX and GeneralsXZH
using libcurl + SDL thread. On tagged release builds, checks the latest
release tag from the GitHub API and shows a dynamic button at bottom-right
of the main menu if a newer version is available.

- Add UpdateChecker.h/.cpp to both Generals/ and GeneralsMD/ with
  libcurl-based async background check (SDL_CreateThread + SDL_AtomicInt)
- Guard feature behind SAGE_UPDATE_CHECK cmake flag (auto-ON with SAGE_USE_SDL3)
- Add curl.cmake and libcurl dependency (vcpkg, sectransp SSL on macOS)
- Add m_checkForUpdates to GlobalData for both games
- Replace hardcoded ButtonGetUpdate (WND position 622,32) with a dynamically
  created push button via gogoGadgetPushButton at bottom-right using real
  screen coordinates (TheDisplay->getWidth/Height())
- Apply Arial 10pt font via winFindFont to avoid oversized WND default font
- Show 'Update available: vX.X' text + tooltip 'click to download' on hover
- SDL_OpenURL to https://github.com/fbraz3/GeneralsX/releases on click
- Support GENERALS_FORCE_UPDATE_CHECK env var for testing in non-debug builds
- Update CMakeLists for both GameEngine and GameEngineDevice targets
- Update flatpak YMLs (com.fbraz3.GeneralsX, com.fbraz3.GeneralsXZH) with curl
- Rename CLAUDE.md to AGENTS.md for AI agent compatibility

Closes #51
…-tag CI job

- Move UpdateChecker.h/.cpp from Generals/ and GeneralsMD/ to Core/GameEngine/
  as shared INTERFACE source, eliminating duplication between the two games
- Fix git_watcher.cmake: use 'describe --exact-match --tags' instead of
  'describe --tags' so GitTag is only set on exact tagged commits, never on
  dev builds that have a release tag somewhere in history
- Add create-tag CI job to release.yml that runs BEFORE build jobs, ensuring
  the tag exists in the repository when binaries are compiled (GitTag is set)
- Remove CURL link entries from both GameEngineDevice CMakeLists (now
  propagated transitively via corei_gameengine_public INTERFACE)
Moving the dry_run guard from the inner step to the job level so the
entire job is skipped (status: skipped) rather than running and doing
nothing. Build jobs treat a skipped dependency as success and proceed
normally. No tag should ever be created or pushed in a dry run.
…g is set

GENERALS_FORCE_UPDATE_CHECK=1 was bypassing the guard in start() but not
the second GitTag[0] == '\0' check inside threadFunc, causing the thread
to return early and never set s_hasUpdate. In force mode, treat any
non-empty tag from the GitHub API response as 'update available'.
Add cleanup-tag-on-failure job that runs when create-tag succeeded but
any downstream job (builds or prepare-release) failed. This prevents
the 'tag already exists' error on retry with the same version.

Condition: failure() && needs.create-tag.result == 'success'
- Skipped on dry_run (create-tag is skipped, result != 'success')
- Skipped when everything succeeds (failure() is false)
- Runs only when tag was pushed but builds/release preparation failed
SDL3/SDL.h was included directly in UpdateChecker.h for the private
static member types (SDL_Thread*, SDL_AtomicInt). This caused build
failures on Linux CI and macOS where MainMenu.cpp (part of g_gameengine)
includes UpdateChecker.h but g_gameengine does not have SDL3 on its
direct include path -- SDL3 only enters via g_gameenginedevice.

Fix: convert class static members and threadFunc to file-scoped statics
in UpdateChecker.cpp. The header now exposes only the public interface
(start, poll, getReleasesUrl) with no SDL3 types, following pimpl-lite
pattern for static-only utility classes.
…ATE_CHECK builds

MainMenu.cpp includes <SDL3/SDL.h> directly to call SDL_OpenURL. On macOS
the build passed because vcpkg adds SDL3 headers globally as an isystem
path. On Linux CI (linux64-deploy), SDL3 comes from FetchContent and its
include directories are only propagated to targets that explicitly depend
on SDL3::SDL3 -- g_gameengine and z_gameengine did not.

Fix: add SDL3::SDL3 as an INTERFACE dependency of corei_gameengine_public
inside the SAGE_UPDATE_CHECK block. Since SAGE_UPDATE_CHECK is only ON
when SAGE_USE_SDL3 is also ON, SDL3::SDL3 always exists at this point.
The include dirs propagate transitively to all gameengine consumers.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an in-game update notification feature for the SDL3 cross-platform builds by querying GitHub Releases in the background, and wires it into both Generals and Zero Hour main menus. This also adjusts build/release plumbing so tagged builds embed GitTag consistently for gating the feature.

Changes:

  • Introduce UpdateChecker (Core) using SDL thread + libcurl against releases/latest, with Options.ini opt-out and main menu UI hook.
  • Add/propagate build flags and dependencies (SAGE_UPDATE_CHECK, curl via vcpkg/CMake, flatpak enablement, presets).
  • Update release workflow/tag embedding behavior (git describe --exact-match) and rename AI agent instructions file.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
Core/GameEngine/Include/Common/UpdateChecker.h Declares the update-checker interface (guarded by SAGE_UPDATE_CHECK).
Core/GameEngine/Source/Common/UpdateChecker.cpp Implements background GitHub release check via libcurl + SDL thread and polling API.
Core/GameEngine/CMakeLists.txt Propagates UpdateChecker source and links curl/SDL3 when enabled.
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp Starts/polls update check and creates a dynamic “Update available” button.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp Same main-menu integration for Zero Hour.
Generals/Code/GameEngine/Include/Common/GlobalData.h Adds m_checkForUpdates toggle.
Generals/Code/GameEngine/Source/Common/GlobalData.cpp Parses CheckForUpdates from Options.ini and sets default TRUE.
GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h Adds m_checkForUpdates toggle (ZH).
GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp Parses CheckForUpdates from Options.ini and sets default TRUE (ZH).
cmake/config-build.cmake Adds SAGE_UPDATE_CHECK option and compile definition; auto-enables with SDL3.
cmake/curl.cmake find_package(CURL CONFIG REQUIRED) when update-check enabled.
CMakeLists.txt Includes cmake/curl.cmake.
CMakePresets.json Enables SAGE_UPDATE_CHECK in linux/macOS presets.
vcpkg.json Adds curl dependency for non-Windows builds.
flatpak/com.fbraz3.GeneralsX.yml Enables SAGE_UPDATE_CHECK for flatpak build.
flatpak/com.fbraz3.GeneralsXZH.yml Enables SAGE_UPDATE_CHECK for flatpak build.
resources/gitinfo/git_watcher.cmake Uses git describe --exact-match --tags so GitTag is set only on tagged commits.
.github/workflows/release.yml Adds a pre-build tag creation job and tag cleanup on failure.
AGENTS.md / CLAUDE.md Renames agent instructions file to AGENTS.md.
.vscode/extensions.json Updates VS Code extension recommendations.
docs/DEV_BLOG/2026-04-DIARY.md Adds diary entry describing the change.
Generals/Code/GameEngine/CMakeLists.txt Notes UpdateChecker now lives in Core (no functional change).
GeneralsMD/Code/GameEngine/CMakeLists.txt Notes UpdateChecker now lives in Core (no functional change).
Generals/Code/GameEngineDevice/CMakeLists.txt Comment about curl transitive linkage.
GeneralsMD/Code/GameEngineDevice/CMakeLists.txt Comment about curl transitive linkage.

Comment thread Core/GameEngine/Source/Common/UpdateChecker.cpp
Comment thread Core/GameEngine/Source/Common/UpdateChecker.cpp
Comment thread Core/GameEngine/Source/Common/UpdateChecker.cpp Outdated
Comment thread Core/GameEngine/Source/Common/UpdateChecker.cpp
Comment thread cmake/config-build.cmake Outdated
Comment thread Core/GameEngine/Source/Common/UpdateChecker.cpp
Comment thread vcpkg.json
Comment thread AGENTS.md Outdated
- UpdateChecker: init s_done to 1 so poll() returns false on early exit
- UpdateChecker: add CURLOPT_NOSIGNAL=1L for POSIX thread safety
- UpdateChecker: call curl_global_init on main thread before thread creation
- UpdateChecker: call SDL_DetachThread immediately after SDL_CreateThread
- UpdateChecker: replace semver comparison with date-based logic using
  published_at vs GitCommitTimeStamp (parseISO8601 + timegm/_mkgmtime)
- config-build.cmake: use if(NOT DEFINED CACHE{}) pattern so explicit
  -DSAGE_UPDATE_CHECK=OFF is respected
- release.yml: move dry_run guard to step level in create-tag job so
  build jobs are never skipped on dry runs
- release.yml: remove prepare-release from cleanup-tag-on-failure needs
  and use always() with explicit per-job failure checks
- MainMenu.cpp (Generals + ZH): destroy and null updateNotifyButton in
  MainMenuShutdown to prevent dangling window handle
- vcpkg-lock.json: add curl 8.13.0 entry (baseline arm64-osx)
- AGENTS.md: fix stray space in git-commit.instructions.md reference
@fbraz3 fbraz3 force-pushed the feature/update-checker branch from 47775a2 to 84e0a2a Compare April 22, 2026 01:54
@fbraz3 fbraz3 merged commit abc3118 into main Apr 22, 2026
8 checks passed
@fbraz3 fbraz3 deleted the feature/update-checker branch April 22, 2026 02:10
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.

Version check and update notification at startup

2 participants