Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
3a7c86f
feat(ngmp): setup branch and instruction context for generals online
fbraz3 Aug 3, 2026
2d20e87
feat(ngmp): implement core ngmp online services manager and ui hooks
fbraz3 Aug 3, 2026
cb58add
feat(ngmp): add dynamic server host and port resolution via environme…
fbraz3 Aug 3, 2026
186f8da
feat(ngmp): set exact default ports 9001 for HTTP/WS and 9000 for HTT…
fbraz3 Aug 3, 2026
a991c7f
feat(ngmp): hardcode server host and ssl configuration at build-time
fbraz3 Aug 4, 2026
b460fc5
feat(ngmp): async http requests, websocket chat, and lobby ui hook
fbraz3 Aug 4, 2026
1c106aa
fix(gui): bypass legacy gamespy timeout and hook ngmp login
fbraz3 Aug 4, 2026
f7a2380
fix(gui): guard legacy gamespy disconnect popups under SAGE_USE_NGMP
fbraz3 Aug 4, 2026
cff882e
Merge remote-tracking branch 'origin/main' into feat/generals-online-…
fbraz3 Aug 4, 2026
77262ef
fix(ngmp): pump event queue in WOLLoginMenuUpdate to process auth eve…
fbraz3 Aug 5, 2026
e46337e
feat(ngmp): implement UI binding for lobby list and chat
fbraz3 Aug 5, 2026
bae2778
fix(ngmp): load endpoint config from env vars at runtime
fbraz3 Aug 5, 2026
2aac27d
fix(ngmp): inject endpoint configuration via CMake compile definitions
fbraz3 Aug 5, 2026
801c8d6
fix(ngmp): bypass GameSpy patch check when NGMP is enabled
fbraz3 Aug 5, 2026
411d46b
fix: UB in obfuscate() crashing on ARM64
fbraz3 Aug 5, 2026
efd9493
feat(ngmp): browser gamecode login flow via SDL_OpenURL + CheckLogin …
fbraz3 Aug 6, 2026
1e42258
fix(ngmp): fix build errors - NGMP_CLIENT_ID in header, WOLLoginMenu …
fbraz3 Aug 6, 2026
606a0e0
feat(ngmp): implement player and global stats fetching
fbraz3 Aug 7, 2026
5d0955c
fix(ngmp): join global lobby network room on Custom Match start
fbraz3 Aug 7, 2026
034023d
fix(ngmp): prevent SIGSEGV on 'Create Game' via staging room init ord…
fbraz3 Aug 13, 2026
c48fb4c
fix(ngmp): separate UI events from GameEngine internal tick update
fbraz3 Aug 13, 2026
ebd949f
fix(ngmp): populate player slot identity and fix Setup Menu Back button
fbraz3 Aug 13, 2026
80e4336
fix(ngmp): support pascal case json keys for lobby list parsing
fbraz3 Aug 13, 2026
0c7527b
fix(ngmp): synchronize curl_ws_recv to prevent libcurl crash
fbraz3 Aug 14, 2026
364381a
fix(ngmp): populate player identity, hide local ping, and return to l…
fbraz3 Aug 14, 2026
b1b7f22
docs(worklog): update 2026-08-DIARY with NGMP libcurl and lobby UX fixes
fbraz3 Aug 14, 2026
33c7751
feat(ngmp): populate lobby roster and restore online welcome voice line
fbraz3 Aug 14, 2026
751583e
fix(build): resolve base game inclusion and offline flatpak json depe…
fbraz3 Aug 14, 2026
f1dc175
fix(ngmp): defer online login flow to user action and propagate CI se…
fbraz3 Aug 14, 2026
abb3505
fix(build): ensure Flatpak sandbox loads CI server secrets and dedupl…
fbraz3 Aug 15, 2026
9fe8170
chore(build): add cmake/ngmp_env.cmake to .gitignore
fbraz3 Aug 15, 2026
672319f
fix(build): remove unsupported env flag from flatpak-builder command
fbraz3 Aug 15, 2026
b8f5f6b
fix(ngmp): fix lobby list deserialization and websocket frame reassembly
fbraz3 Aug 16, 2026
3e3fac3
fix(ngmp): restore staging room synchronization and map selection
fbraz3 Aug 16, 2026
4ba745f
feat(online): implement ngmp architecture bridge and menu alignment
fbraz3 Aug 16, 2026
b074bc6
feat(online): address lobby and match room gaps with reference parity
fbraz3 Aug 16, 2026
9af8557
feat(ngmp): resolve lobby menu functional gaps
fbraz3 Aug 16, 2026
de201c5
fix(build): resolve Linux GCC template error from GameSpy min/max macros
fbraz3 Aug 16, 2026
c188899
feat(ngmp): reconcile multiplayer GUI menus with reference
fbraz3 Aug 16, 2026
3bc4418
feat(ngmp): fix staging room map selection, host controls, and websoc…
fbraz3 Aug 18, 2026
8062424
fix(ngmp): synchronize multiplayer staging room, map paths, and guest…
fbraz3 Aug 18, 2026
3e71605
fix(ngmp): populate lobby members cache and fix map listbox initializ…
fbraz3 Aug 18, 2026
905f56a
chore(sync): merge origin/main into feat/generals-online-ngmp
fbraz3 Aug 18, 2026
8c4de02
fix(network): isolate NGMPGame dependency from Core InGameChat
fbraz3 Aug 18, 2026
73ca7ee
fix(gui): resolve null pointer crash in map select menu initialization
fbraz3 Aug 19, 2026
ac1f367
fix(ngmp): fix staging room synchronization and segfaults
fbraz3 Aug 19, 2026
b679744
chore(merge): sync feat/generals-online-ngmp with origin/main
fbraz3 Aug 21, 2026
aafaf48
build(windows): enable curl dependency on Windows for NGMP support
fbraz3 Aug 21, 2026
55bd24d
fix(platform): add cross-platform NGMP OpenURL helper for Windows MSVC
fbraz3 Aug 21, 2026
8e97178
fix(build): protect nlohmann json from snprintf macro pollution on MSVC
fbraz3 Aug 21, 2026
db9eb5b
fix(build): fix C4189 unused variable and restore std::snprintf in fa…
fbraz3 Aug 21, 2026
46d1eb5
fix(engine): guard NGMP against headless mode and replace GameSpy thr…
fbraz3 Aug 22, 2026
a467d2d
Resolve merge conflicts in 1 file(s)
coderabbitai[bot] Aug 22, 2026
4d92264
fix(ci): restore executable permissions and invoke flatpak build scri…
fbraz3 Aug 22, 2026
ef691ae
chore(merge): sync feat/generals-online-ngmp with origin/main
fbraz3 Aug 22, 2026
e7f4cfa
fix(ngmp): initialize rank and ladder singletons and guard null acces…
fbraz3 Aug 22, 2026
b2cd011
ci(workflows): pass ngmp secrets to windows and linux build jobs
fbraz3 Aug 23, 2026
acc24bb
fix(ngmp): provide dummy GameSpy message queues to prevent UI crashes
fbraz3 Aug 23, 2026
a4c77aa
docs(ngmp): establish GameSpy online discontinuation policy
fbraz3 Aug 23, 2026
6f560b8
fix(ngmp): address CodeRabbit review feedback on safety and concurrency
fbraz3 Aug 23, 2026
8054ec7
feat(ngmp): synchronize staging room options, start positions, and pa…
fbraz3 Aug 24, 2026
6ad3aa0
fix(ngmp): send lobby leave request on host disconnect to prevent pha…
fbraz3 Aug 25, 2026
94caa1a
feat(ngmp): implement staging room host migration in parity with refe…
fbraz3 Aug 25, 2026
d955209
fix(ngmp): unlock host UI elements on host migration in staging room
fbraz3 Aug 25, 2026
a7971cb
docs(worklog): update diary with staging room host migration and phan…
fbraz3 Aug 25, 2026
7b0ceff
fix(ngmp): replicate guest faction and color, fix ai slots and unfree…
fbraz3 Aug 25, 2026
d9bb800
fix(ngmp): hide static text overlay and unlock player slots on host m…
fbraz3 Aug 25, 2026
ba03cd8
fix(ngmp): disband staging room and return guests to lobby when host …
fbraz3 Aug 25, 2026
a28ffd1
feat(ngmp): add universal Enter submission, password padlock, and sta…
fbraz3 Aug 26, 2026
451ebed
chore(merge): sync feat/generals-online-ngmp with origin/main
fbraz3 Aug 28, 2026
a3c3377
feat(ngmp): implement simultaneous game start and post-game statistic…
fbraz3 Aug 28, 2026
63b9e81
fix(ngmp): restore portable JSON include
arazmj Aug 28, 2026
dc9340f
feat(ngmp): add p2p transport mesh and fix guest start crash
fbraz3 Aug 30, 2026
c2c549f
Resolve merge conflicts in 1 file(s)
coderabbitai[bot] Aug 30, 2026
4225d22
fix(ci): add protobuf to macos workflow and add fallback transport gu…
fbraz3 Aug 31, 2026
5623127
fix(ci): add abseil for macos protobuf and remove steam header from t…
fbraz3 Aug 31, 2026
0187ba8
fix(ci): bump GameNetworkingSockets to v1.6.0 and set C++20 standard
fbraz3 Aug 31, 2026
599abc8
docs(worklog): document P2P transport and cross-platform CI fixes
fbraz3 Aug 31, 2026
a005afb
fix(build): prioritize canonical Homebrew protobuf and set protoc paths
fbraz3 Aug 31, 2026
9b0e8c8
feat(ngmp): enable end-to-end online multiplayer match flow and P2P t…
fbraz3 Sep 1, 2026
07c3530
fix(build): remove Zero Hour NGMP headers from Generals base GameLogic
fbraz3 Sep 1, 2026
fc008bc
docs(worklog): add diary entry for September 2026 build fix
fbraz3 Sep 1, 2026
c883462
chore: restore sh scripts exec permissions
fbraz3 Sep 1, 2026
b3a6e16
feat(ngmp): implement QuickMatch ranked matchmaking
fbraz3 Sep 1, 2026
8c00a56
fix(ngmp): add auth header and full dictionary parsing for matchmakin…
fbraz3 Sep 1, 2026
b4d854e
fix(ngmp): fix anticheat_id in lobby join and guard shutdown handlers…
fbraz3 Sep 2, 2026
e103f0e
docs(diary): log quickmatch lobby join and shutdown crash fixes
fbraz3 Sep 2, 2026
c27c725
fix(ngmp): bypass legacy map transfer in NGMP and improve quickmatch …
fbraz3 Sep 2, 2026
0f936bb
docs(diary): log map validation and UI button handling fixes
fbraz3 Sep 2, 2026
30c621f
fix(map): robust slash-agnostic and base-leaf map search in MapCache:…
fbraz3 Sep 2, 2026
6810a50
docs(diary): update worklog with map normalization details
fbraz3 Sep 2, 2026
1e308c6
fix(platform): normalize user preference filepaths and enforce direct…
fbraz3 Sep 2, 2026
98ae3e9
docs(diary): log UserPreferences POSIX path normalization
fbraz3 Sep 2, 2026
c2c828a
fix(map): scan user Documents Maps folders on POSIX and add disk fall…
fbraz3 Sep 2, 2026
9ba1f31
docs(diary): update worklog with maps multi-directory scanning and di…
fbraz3 Sep 2, 2026
74f9b2c
fix(preferences): preserve filenames on POSIX load and ensure trailin…
fbraz3 Sep 2, 2026
0ed98aa
feat(ngmp): implement production browser auth, update enforcement, an…
fbraz3 Sep 3, 2026
7bf0789
ci(ngmp): pass web portal and motd repository secrets to build workflows
fbraz3 Sep 3, 2026
fa51e22
fix(ngmp): fix header ordering for gcc 15 and use std::format for msvc
fbraz3 Sep 3, 2026
8a0f7b6
fix(ngmp): request server-registered LoginCode and recover main menu …
fbraz3 Sep 3, 2026
1d0b852
fix(ngmp): allow empty colored separator lines in motd parser
fbraz3 Sep 3, 2026
d4ff536
fix(ngmp): address CodeRabbit review feedback on PR #260
fbraz3 Sep 3, 2026
87482d9
fix(ngmp): harden thread safety, annotations and quickmatch state
fbraz3 Sep 4, 2026
5f159f1
fix(ngmp): poll MOTD events in welcome menu and update annotations
fbraz3 Sep 4, 2026
487be72
fix(ngmp): use non-destructive MOTD state check in welcome menu
fbraz3 Sep 4, 2026
95c1fda
feat(ngmp): add clickable URL support to MOTD in welcome menu
fbraz3 Sep 4, 2026
28034d0
fix(ngmp): resolve lobby join anticheat mismatch and windows preferen…
fbraz3 Sep 5, 2026
caa5383
fix(windows): include <filesystem> for user preferences directory cre…
fbraz3 Sep 5, 2026
80502e9
fix(network): enable P2P GameNetworkingSockets on Windows and configu…
fbraz3 Sep 5, 2026
b1acfaf
fix(ci): use sparse checkout for ValveNetworkingSockets in Windows build
fbraz3 Sep 5, 2026
2440e47
fix(ci): bundle MSVC debug CRT DLLs required by x86 GNS dependencies
fbraz3 Sep 5, 2026
192c850
fix(ci): delay-load GameNetworkingSockets on Windows and use desktop-…
fbraz3 Sep 6, 2026
f54642b
docs(worklog): document successful CI pipeline run with Windows GNS d…
fbraz3 Sep 6, 2026
fb2a5be
fix(ci): strictly filter x86 CRT DLLs for Windows package
fbraz3 Sep 6, 2026
7f0eb4b
fix(network): integrate gamenetworkingsockets via vcpkg for clean rel…
fbraz3 Sep 6, 2026
6f79881
fix(ci): remove invalid ice feature flag from gamenetworkingsockets v…
fbraz3 Sep 6, 2026
9499910
ci(windows): add vcpkg binary caching and streamline runtime collection
fbraz3 Sep 6, 2026
569a196
fix(build): unify uint32 typedef to uint32_t to fix MSVC C2371 redefi…
fbraz3 Sep 6, 2026
8f05f35
fix(ngmp): tick services during load screen and transport updates
fbraz3 Sep 6, 2026
81fc253
fix(ngmp): sanitize server hostnames and sensitive tokens in client logs
fbraz3 Sep 6, 2026
e4b7568
fix(ngmp): fix ICE STUN/TURN server configuration and immediate signa…
fbraz3 Sep 7, 2026
9a6cd83
fix(ngmp): disable incompatible TURN relay to prevent GNS thinker sta…
fbraz3 Sep 7, 2026
d33338e
fix(ngmp): disable STUN servers to avoid runaway keepalive loop on tu…
fbraz3 Sep 7, 2026
47ad501
fix(ngmp): configure STUN and Cloudflare Calls TURN relay for WAN con…
fbraz3 Sep 8, 2026
99c9b52
Merge remote-tracking branch 'origin/main' into feat/generals-online-…
fbraz3 Sep 9, 2026
f9e0554
fix(ngmp): resolve CodeRabbit review findings on URL opening, auth, a…
fbraz3 Sep 9, 2026
7e254c3
fix(gui): include SDL3/SDL.h on Windows in WOLWelcomeMenu for SDL_Ope…
fbraz3 Sep 9, 2026
a42b933
fix(gui): restore ShellExecute for Windows when SDL3 is unavailable
fbraz3 Sep 9, 2026
2d4196b
ci: add vcpkg binary caching with vcpkg-lock.json hashing to Windows …
fbraz3 Sep 9, 2026
7c87ce7
chore(merge): sync origin/main into feat/generals-online-ngmp and res…
fbraz3 Sep 9, 2026
90d92a9
fix(ngmp): resolve GNS ICE thinker loop and restore reference STUN/TU…
fbraz3 Sep 9, 2026
1d1d98c
fix(ngmp): configure Cloudflare Calls TURN and STUN with paired crede…
fbraz3 Sep 9, 2026
ed86564
fix(ci): apply gns ice thinker patch to windows vcpkg overlay port
fbraz3 Sep 9, 2026
959572d
fix(network): resolve ICE address family mismatch and loading timeout
fbraz3 Sep 10, 2026
209cd9c
fix(ngmp): refresh global stats dynamically and use authenticated use…
fbraz3 Sep 10, 2026
d6d48c6
fix(ngmp): enable gamenetworkingsockets on linux flatpak and docker b…
fbraz3 Sep 10, 2026
4ef011a
fix(ngmp): resolve coderabbit findings and fix linux gcc int64 conflict
fbraz3 Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/instructions/ngmp.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
applyTo: '**/GeneralsOnline/**,**/NextGenMP/**'
---

# NGMP Subsystem Implementation Instructions

These instructions govern the Next-Gen Multiplayer (NGMP) client protocol integration into **GeneralsX**. All changes in `GeneralsOnline` and `NextGenMP` components must adhere strictly to these guidelines.

> [!IMPORTANT]
> **GameSpy Online is being discontinued.** NGMP is the sole multiplayer backend going forward. Compatibility with the legacy GameSpy online flow is **NOT a goal** and must NOT block NGMP development. If making NGMP work requires changing or breaking legacy GameSpy online code paths, that is acceptable and expected. Only the following non-online modes must remain fully functional: **Singleplayer campaigns**, **Skirmish vs AI**, and **LAN multiplayer**.

---

## GameSpy Compatibility Policy

| Mode | Required to keep working | Notes |
|---|---|---|
| Singleplayer / Campaign | ✅ Yes | Must not regress |
| Skirmish (vs AI, offline) | ✅ Yes | Must not regress |
| LAN Multiplayer | ✅ Yes | Must not regress |
| GameSpy Online (WOL) | ❌ No | Being discontinued; may be broken by NGMP changes |
| GameSpy P2P auth/serial | ❌ No | Superseded by NGMP auth |

When a piece of legacy UI code is in the critical path for NGMP but also serves the legacy GameSpy online flow:
- **Modify it** to work correctly with NGMP, even if that removes or changes the GameSpy online behavior.
- If a function or callback is **only** called in the GameSpy online context (never touched in singleplayer, skirmish, or LAN), it may be stubbed out, simplified, or removed to unblock NGMP work.
- Prefer **replacing** legacy `TheGameSpy*` queue calls with equivalent NGMP interface calls rather than maintaining both paths in parallel.
- Dummy/blackhole implementations (like dummy message queues) are a **last resort** workaround — prefer proper NGMP replacements whenever feasible.

---

## Golden Constraints

1. **Strict Prohibition of `<windows.h>` & Win32 APIs**:
- Never include `<windows.h>`, `<winsock2.h>`, `<ws2tcpip.h>`, `<wincred.h>`, or `<shellapi.h>` in NGMP sources.
- Use standard C++ (`<chrono>`, `<thread>`, `<mutex>`), POSIX networking (`<sys/socket.h>`, `<netinet/in.h>`, `<arpa/inet.h>`), or SDL3 primitives (`SDL_GetTicks()`, `SDL_Delay()`, `SDL_GetPrefPath()`).

2. **Platform Layer Isolation**:
- Low-level network socket and OS calls must reside exclusively in `Core/GameEngineDevice/`.
- Higher-level network logic under `GeneralsMD/Code/GameEngine/Source/GameNetwork/GeneralsOnline/` must use pure abstract interfaces and cross-platform abstractions.

3. **Thread Safety & UI Main Thread Separation**:
- Network callbacks from HTTP/WebSocket/P2P threads **MUST NOT** directly access or mutate UI controls (`GameWindow`, `WOLLobbyMenu`, etc.).
- Network events must be posted to a thread-safe thread/FIFO queue, consumed strictly on the main render thread during `Shell::update()`.

4. **Credential & Token Storage**:
- Do NOT use Windows Credential Manager.
- Save session JWT tokens in an obfuscated local configuration file under the directory provided by `SDL_GetPrefPath("GeneralsX", "GeneralsOnline")`.

5. **Server Endpoint Environment**:
- Development backend server target: `ws://192.168.1.120:9001/ws` (WebSocket) and `http://192.168.1.120:9001/api` (REST).
- Server URLs must be configurable via INI/JSON config rather than hardcoded string literals.

6. **Cross-Platform Math & Endianness**:
- Ensure network packet data serialization handles network byte order (`htons`/`ntohs`, `htonl`/`ntohl`) explicitly to support cross-play between x86_64 Linux and ARM64 macOS.

7. **Reference repositories**:
- There are two reference repositories for NGMP, take a look on these to understand the protocol and implementation details:
1. `references/GameClient`: The client-side implementation.
2. `references/GameServer`: The server-side implementation.

8. **REST API & Routing Conventions (Lessons Learned)**:
- **Environment/Contract Prefix**: The C# Kestrel backend strictly requires environment and contract version routing. Do **NOT** use `GetServerRESTEndpoint()` directly to build URLs. Always use `NGMP::GetAPIEndpoint("EndpointName")` (e.g. `NGMP::GetAPIEndpoint("Lobbies")`), which appends the required `/env/dev/contract/1/` prefix automatically.
- **Authorization**: Almost all endpoints require authorization. You must inject the `Authorization: Bearer <token>` HTTP header in all requests (GET, POST, PUT, DELETE). Missing this will result in a `401 Unauthorized` response.
- **HTTP Methods & Payloads**: Backend endpoints have strict method bindings (`[HttpGet]`, `[HttpPut]`, `[HttpPost]`). For example, creating a lobby requires `PUT /Lobbies`, while joining a lobby requires `PUT /Lobby/{id}`. The JSON payloads must also contain all expected fields (even if default or empty), or the server will reject the request with `400 Bad Request`.

9. **WebSocket Protocol Conventions (Lessons Learned)**:
- **Message ID Routing**: The backend `WebSocketController` strictly uses integer `msg_id` values to route actions, NOT string action types (e.g., `msg_id: 1` for sending chat, `msg_id: 2` for receiving chat, `msg_id: 4` for member list updates). Always include the correct `msg_id` in sent JSON payloads and use it to parse incoming messages.
- **Network Rooms (Lobby Registration)**: The backend requires the client to explicitly register into a network room to receive lobby and player updates. When initializing the global lobby chat, you must send `{"msg_id": 3, "room": 0}` (NETWORK_ROOM_CHANGE_ROOM). If you fail to do this, the server considers you in room `-1`, which causes the HTTP `GET /Lobbies` endpoint to return `0 lobbies` (as it filters by your current room) and prevents you from receiving player list updates (`msg_id: 4`).
- **Heartbeats**: The WebSocket server automatically drops connections if a PING is not received within a timeout window (often 20s). The client must periodically send `{"msg_id": 8}` (ping) at least every 10 seconds to keep the socket alive.
9 changes: 8 additions & 1 deletion .github/workflows/build-linux-flatpak.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ jobs:
flatpak-builder-${{ runner.os }}-${{ inputs.preset }}-

- name: Build Flatpak Bundle
env:
NGMP_DEFAULT_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_PORT: ${{ secrets.NGMP_SERVER_PORT }}
NGMP_USE_SSL: ${{ secrets.NGMP_USE_SSL }}
NGMP_WEB_PORTAL_URL: ${{ secrets.NGMP_WEB_PORTAL_URL }}
NGMP_MOTD_URL: ${{ secrets.NGMP_MOTD_URL }}
run: |
set -o pipefail
mkdir -p logs
./scripts/build/linux/build-linux-flatpak.sh "${{ inputs.preset }}" "${{ matrix.game.value }}" 2>&1 | tee "logs/build_flatpak_${{ matrix.game.log_suffix }}.log"
bash ./scripts/build/linux/build-linux-flatpak.sh "${{ inputs.preset }}" "${{ matrix.game.value }}" 2>&1 | tee "logs/build_flatpak_${{ matrix.game.log_suffix }}.log"

- name: Locate generated Flatpak bundle
id: bundle
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ jobs:
echo "✅ vcpkg ready at: /opt/vcpkg"

- name: Configure CMake (Linux)
env:
NGMP_DEFAULT_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_PORT: ${{ secrets.NGMP_SERVER_PORT }}
NGMP_USE_SSL: ${{ secrets.NGMP_USE_SSL }}
NGMP_WEB_PORTAL_URL: ${{ secrets.NGMP_WEB_PORTAL_URL }}
NGMP_MOTD_URL: ${{ secrets.NGMP_MOTD_URL }}
run: |
mkdir -p logs
cmake --preset ${{ inputs.preset }} 2>&1 | tee logs/configure_linux.log
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ jobs:

# Freetype + Fontconfig (required by font rendering)
brew install freetype fontconfig

# Protobuf, Abseil & OpenSSL (required by GameNetworkingSockets / NGMP P2P transport)
brew install protobuf abseil openssl@3

# NOTE: GLM is intentionally NOT installed via Homebrew.
# It is fetched via CMake FetchContent in cmake/glm.cmake (tag 1.0.3).
Expand Down Expand Up @@ -356,6 +359,12 @@ jobs:
CXX: clang++
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
NGMP_DEFAULT_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_PORT: ${{ secrets.NGMP_SERVER_PORT }}
NGMP_USE_SSL: ${{ secrets.NGMP_USE_SSL }}
NGMP_WEB_PORTAL_URL: ${{ secrets.NGMP_WEB_PORTAL_URL }}
NGMP_MOTD_URL: ${{ secrets.NGMP_MOTD_URL }}
run: |
mkdir -p logs
echo "VULKAN_SDK=$VULKAN_SDK"
Expand Down
36 changes: 31 additions & 5 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/vcpkg-bincache
key: vcpkg-bincache-${{ runner.os }}-x86-${{ hashFiles('vcpkg.json', 'vcpkg-lock.json') }}
key: vcpkg-bincache-${{ runner.os }}-x86-${{ hashFiles('vcpkg.json', 'vcpkg-lock.json', 'cmake/ports/**') }}
restore-keys: |
vcpkg-bincache-${{ runner.os }}-x86-

Expand All @@ -73,14 +73,23 @@ jobs:
New-Item -ItemType Directory -Force -Path $cacheDir | Out-Null
"VCPKG_DEFAULT_BINARY_CACHE=$cacheDir" >> $env:GITHUB_ENV
"VCPKG_BINARY_SOURCES=clear;files,$cacheDir,readwrite" >> $env:GITHUB_ENV
"VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/triplets" >> $env:GITHUB_ENV
"VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/cmake/triplets" >> $env:GITHUB_ENV
"VCPKG_OVERLAY_PORTS=${{ github.workspace }}/cmake/ports" >> $env:GITHUB_ENV
"VCPKG_INSTALL_OPTIONS=--x-abi-tools-use-exact-versions" >> $env:GITHUB_ENV

- name: Configure
shell: pwsh
env:
NGMP_DEFAULT_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_HOST: ${{ secrets.NGMP_DEFAULT_HOST }}
NGMP_SERVER_PORT: ${{ secrets.NGMP_SERVER_PORT }}
NGMP_USE_SSL: ${{ secrets.NGMP_USE_SSL }}
NGMP_WEB_PORTAL_URL: ${{ secrets.NGMP_WEB_PORTAL_URL }}
NGMP_MOTD_URL: ${{ secrets.NGMP_MOTD_URL }}
run: |
$env:VCPKG_ROOT = $env:VCPKG_INSTALLATION_ROOT
cmake --preset win32-vcpkg `
-DVCPKG_OVERLAY_PORTS="${{ github.workspace }}/cmake/ports" `
-DRTS_BUILD_GENERALS=${{ matrix.game.build_generals }} `
-DRTS_BUILD_ZEROHOUR=${{ matrix.game.build_zh }} `
-DRTS_BUILD_CORE_TOOLS=OFF `
Expand All @@ -95,7 +104,7 @@ jobs:
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/vcpkg-bincache
key: ${{ steps.vcpkg_cache.outputs.cache-primary-key || format('vcpkg-bincache-{0}-x86-{1}', runner.os, hashFiles('vcpkg.json', 'vcpkg-lock.json')) }}
key: ${{ steps.vcpkg_cache.outputs.cache-primary-key || format('vcpkg-bincache-{0}-x86-{1}', runner.os, hashFiles('vcpkg.json', 'vcpkg-lock.json', 'cmake/ports/**')) }}

- name: Build
shell: pwsh
Expand All @@ -108,9 +117,26 @@ jobs:
New-Item -ItemType Directory -Force ci-artifacts | Out-Null
Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.exe }} artifacts/
Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.pdb }} artifacts/
Copy-Item build/win32-vcpkg/vcpkg_installed/x86-windows/bin/zlib1.dll artifacts/
Copy-Item build/win32-vcpkg/${{ matrix.game.folder }}/Release/${{ matrix.game.exe }} ci-artifacts/
Copy-Item build/win32-vcpkg/vcpkg_installed/x86-windows/bin/zlib1.dll ci-artifacts/

Get-ChildItem -Path @("vcpkg_installed/x86-windows/bin/*.dll", "build/win32-vcpkg/vcpkg_installed/x86-windows/bin/*.dll") -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host "Collecting vcpkg DLL: $($_.FullName)"
Copy-Item $_.FullName artifacts/
Copy-Item $_.FullName ci-artifacts/
}

# Collect Desktop Release CRT DLLs (msvcp140.dll, vcruntime140.dll, etc.)
$vcReleaseDir = Get-ChildItem "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC" -Directory -Recurse |
Where-Object { $_.FullName -match "\\x86\\" -and $_.FullName -match "Microsoft\.VC143\.CRT$" -and $_.FullName -notmatch "onecore" } |
Select-Object -First 1

if ($vcReleaseDir) {
Get-ChildItem -Path (Join-Path $vcReleaseDir.FullName "*.dll") | ForEach-Object {
Write-Host "Collecting Desktop Release CRT DLL: $($_.FullName)"
Copy-Item $_.FullName artifacts/
Copy-Item $_.FullName ci-artifacts/
}
}

foreach ($name in @("binkw32.dll", "mss32.dll")) {
$stub = Get-ChildItem build/win32-vcpkg -Filter $name -Recurse |
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,22 @@ jobs:
with:
preset: linux64-deploy
checkout_ref: refs/tags/${{ inputs.release_version }}
secrets: inherit

build-macos:
needs: [create-tag]
uses: ./.github/workflows/build-macos.yml
with:
preset: macos-vulkan
checkout_ref: refs/tags/${{ inputs.release_version }}
secrets: inherit

build-windows:
needs: [create-tag]
uses: ./.github/workflows/build-windows.yml
with:
checkout_ref: refs/tags/${{ inputs.release_version }}
secrets: inherit

# GeneralsX @build BenderAI 21/04/2026 Roll back the pushed tag when anything
# downstream of create-tag fails, so a re-run with the same version is possible.
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/replay-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ jobs:
$output += $stderrOutput
$output += $stdoutOutput
$output | ForEach-Object { Write-Host $_ }
if (!$timedOut -and $exitCode -is [int] -and $exitCode -ne 0 -and $output.Count -eq 0) {
$hexCode = "0x" + ([int64]$exitCode -band 0xFFFFFFFF).ToString("X8")
Write-Host "Process exited with code $exitCode ($hexCode) and produced no output"
}

$source = if ($replay.Name.StartsWith("linux_")) { "Linux" } elseif ($replay.Name.StartsWith("macos_")) { "macOS" } else { "other" }
$completionLines = @($stdoutOutput |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,6 @@ flatpak/*.flatpak
#tmp and log files on root folder
logs_*.zip
*.log
/cmake/ngmp_env.cmake
references/GameClient/
references/GameServer/
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@
path = references/fbraz3-dxvk
url = https://github.com/fbraz3/dxvk.git
branch = generalsx-macos-v2.6
[submodule "references/GameClient"]
path = references/GameClient
url = https://github.com/GeneralsOnlineDevelopmentTeam/GameClient.git
[submodule "references/GameServer"]
path = references/GameServer
url = https://github.com/fbraz3/GeneralsOnlineGameServer.git
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ GeneralsX is a cross-platform port of Command & Conquer: Generals Zero Hour for
9. **Update worklog** – Update `docs/WORKLOG/YYYY-MM-DIARY.md` before committing (see [.github/instructions/docs.instructions.md](.github/instructions/docs.instructions.md) for details)
10. **Reference repos** – Study patterns, don't copy-paste
11. **Backport to Generals** – Bugfixes and improvements must be backported to the Generals base game.
12. **NGMP over GameSpy** – The legacy GameSpy online backend is being discontinued. NGMP (`GeneralsOnline`) is the sole online multiplayer target. Singleplayer, Skirmish (vs AI), and LAN multiplayer must remain fully functional. GameSpy online compatibility is explicitly NOT required and must NOT block NGMP work. See [ngmp.instructions.md](.github/instructions/ngmp.instructions.md) for the full compatibility policy.

## Cross-Platform Determinism (Mac vs Linux)
To guarantee cross-play between macOS ARM64 and Linux x86_64 without SyncCrash desyncs, you must obey the following rules:
Expand Down Expand Up @@ -289,5 +290,6 @@ The `**` at applyTo means all files, you MUST load it everytime.
| [.github/instructions/platform-macos.instructions.md](.github/instructions/platform-macos.instructions.md) | `scripts/build/macos/**,references/fbraz3-dxvk/**` | macOS/DXVK build notes |
| [.github/instructions/docs.instructions.md](.github/instructions/docs.instructions.md) | `**/*.md` | Documentation structure and workflow |
| [.github/instructions/scripts.instructions.md](.github/instructions/scripts.instructions.md) | `scripts/**` | Script organization and naming |
| [.github/instructions/ngmp.instructions.md](.github/instructions/ngmp.instructions.md) | `**/GeneralsOnline/**,**/NextGenMP/**` | NGMP cross-platform multiplayer integration guidelines |

Update this table when instruction files are added, removed, or renamed.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ include(cmake/miniaudio.cmake)
# curl.cmake is self-guarded with if(SAGE_UPDATE_CHECK), so always safe to include.
include(cmake/curl.cmake)

# GeneralsX @feature GeneralsOnline NGMP protocol dependency setup
include(cmake/ngmp.cmake)

# GeneralsX @feature fbraz 03/05/2026 Phase 4: Deterministic math library integration
# gamemath.cmake integrates fdlibm-based GameMath for cross-platform replay validation.
# Upstream reference: Okladnoj, PR #2670
Expand Down
3 changes: 2 additions & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_MSVC_DEBUG_INFORMATION_FORMAT": "$<$<CONFIG:Release,Debug,RelWithDebInfo>:Embedded>",
"CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL",
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"VCPKG_OVERLAY_PORTS": "${sourceDir}/cmake/ports"
}
},
{
Expand Down
1 change: 0 additions & 1 deletion Core/GameEngine/Include/Common/GameDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/

#pragma once

#include "WWLib/WWDefines.h"

// Note: Retail compatibility must not be broken before this project officially does.
Expand Down
9 changes: 9 additions & 0 deletions Core/GameEngine/Include/Common/UpdateChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ class UpdateChecker
// Returns false if still in progress or no update found.
static bool poll(const char** outLatestTag);

// Returns true if background update check is finished
static bool isDone();

// Returns true if a newer version is available
static bool hasUpdate();

// Returns latest tag string or empty
static const char* getLatestTag();

// GitHub repository used for the API query.
static const char* getReleasesUrl();
};
Expand Down
12 changes: 12 additions & 0 deletions Core/GameEngine/Include/GameNetwork/GameSpy/LobbyUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ enum GameSortType CPP_11(: Int)

Bool HandleSortButton( NameKeyType sortButton );
void PopulateLobbyPlayerListbox();

enum LobbyGameModeFilter CPP_11(: Int)
{
LOBBY_FILTER_ALL = 0,
LOBBY_FILTER_1V1,
LOBBY_FILTER_TEAM,
LOBBY_FILTER_FFA,
LOBBY_FILTER_AOD,
};

extern LobbyGameModeFilter theLobbyFilter;
LobbyGameModeFilter detectGameMode(const std::string& name);
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@

#include "gamespy/gstats/gpersist.h"

#if defined(min)
#undef min
#endif
#if defined(max)
#undef max
#endif

#define MAX_BUDDY_CHAT_LEN 128

typedef std::map<Int, UnsignedInt> PerGeneralMap;
Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameNetwork/GameSpyOverlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ void ClearGSMessageBoxes(); ///< Tear down any GS message boxes (e.g. in case we
void GSMessageBoxOk(UnicodeString titleString,UnicodeString bodyString, GameWinMsgBoxFunc okFunc = nullptr); ///< Display a Message box with Ok button and track it
void GSMessageBoxOkCancel(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc okFunc, GameWinMsgBoxFunc cancelFunc); ///< Display a Message box with Ok/Cancel buttons and track it
void GSMessageBoxYesNo(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc yesFunc, GameWinMsgBoxFunc noFunc); ///< Display a Message box with Yes/No buttons and track it
void GSMessageBoxCancel(UnicodeString title, UnicodeString message, GameWinMsgBoxFunc cancelFunc = nullptr); ///< Display a Message box with Cancel button and track it
void RaiseGSMessageBox(); ///< Bring GS message box to the foreground (if we transition screens while a message box is up)

enum GSOverlayType CPP_11(: Int)
Expand Down
Loading
Loading