Skip to content

Commit 85eca2c

Browse files
committed
feat(math): integrate deterministic GameMath
Replaces standard CRT trigonometric functions with deterministic fdlibm equivalents from GameMath via FetchContent. Ensures multiplayer cross-platform replay determinism. Resolves cross-platform mismatch disconnects.
1 parent cbdb846 commit 85eca2c

5 files changed

Lines changed: 59 additions & 56 deletions

File tree

Core/Libraries/Source/WWVegas/WWMath/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,11 @@ target_link_libraries(core_wwmath PRIVATE
9191
core_wwsaveload
9292
)
9393

94+
if (SAGE_USE_DETERMINISTIC_MATH)
95+
target_link_libraries(core_wwmath PUBLIC
96+
gamemath
97+
)
98+
endif()
99+
94100
# @todo Test its impact and see what to do with the legacy functions.
95101
#add_compile_definitions(core_wwmath PUBLIC ALLOW_TEMPORARIES) # Enables legacy math with "temporaries"

Core/Libraries/Source/WWVegas/WWMath/wwmath.h

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@
4141
#include <float.h>
4242
#include <assert.h>
4343

44+
// Include GameMath headers when deterministic math is enabled
45+
// Use __has_include because wwmath.h is transitively included by targets that may not link gamemath.
46+
// Must be outside class WWMath to prevent injecting global symbols into the class.
47+
#if defined(__has_include) && __has_include("gmath.h")
48+
#include "gmath.h"
49+
#define USE_DETERMINISTIC_MATH (1)
50+
#endif
51+
4452
/*
4553
** Some global constants.
4654
*/
@@ -139,19 +147,10 @@ static WWINLINE float Asin(float val);
139147
// Upstream reference: Okladnoj, PR #2670
140148
// https://github.com/TheSuperHackers/GeneralsGameCode/pull/2670
141149

142-
// Include GameMath headers when deterministic math is enabled
143-
// Note: GameMath integration is pending. This header will be populated when
144-
// GameMath submodule or library is available. For now, wrappers fallback to CRT.
145-
#ifdef USE_DETERMINISTIC_MATH
146-
// TODO: Uncomment when GameMath library is integrated as submodule
147-
// #include "GameMath/deterministic_math.h"
148-
#endif
149-
150150
static WWINLINE float Atan(float x)
151151
{
152152
#ifdef USE_DETERMINISTIC_MATH
153-
// TODO: return GameMath::Atan(x);
154-
return static_cast<float>(atan(x));
153+
return gm_atanf(x);
155154
#else
156155
return static_cast<float>(atan(x));
157156
#endif
@@ -160,8 +159,7 @@ static WWINLINE float Atan(float x)
160159
static WWINLINE float Atan2(float y, float x)
161160
{
162161
#ifdef USE_DETERMINISTIC_MATH
163-
// TODO: return GameMath::Atan2(y, x);
164-
return static_cast<float>(atan2(y, x));
162+
return gm_atan2f(y, x);
165163
#else
166164
return static_cast<float>(atan2(y, x));
167165
#endif
@@ -172,28 +170,25 @@ static WWINLINE float Atan2(float y, float x)
172170
static WWINLINE float SinTrig(float x)
173171
{
174172
#ifdef USE_DETERMINISTIC_MATH
175-
// TODO: return GameMath::Sin(x);
176-
return Sin(x);
173+
return gm_sinf(x);
177174
#else
178-
return Sin(x);
175+
return sinf(x);
179176
#endif
180177
}
181178

182179
static WWINLINE float CosTrig(float x)
183180
{
184181
#ifdef USE_DETERMINISTIC_MATH
185-
// TODO: return GameMath::Cos(x);
186-
return Cos(x);
182+
return gm_cosf(x);
187183
#else
188-
return Cos(x);
184+
return cosf(x);
189185
#endif
190186
}
191187

192188
static WWINLINE float TanTrig(float x)
193189
{
194190
#ifdef USE_DETERMINISTIC_MATH
195-
// TODO: return GameMath::Tan(x);
196-
return tanf(x);
191+
return gm_tanf(x);
197192
#else
198193
return tanf(x);
199194
#endif
@@ -202,34 +197,37 @@ static WWINLINE float TanTrig(float x)
202197
static WWINLINE float ACosTrig(float x)
203198
{
204199
#ifdef USE_DETERMINISTIC_MATH
205-
// TODO: return GameMath::Acos(x);
206-
return Acos(x);
200+
return gm_acosf(x);
207201
#else
208-
return Acos(x);
202+
return acosf(x);
209203
#endif
210204
}
211205

212206
static WWINLINE float ASinTrig(float x)
213207
{
214208
#ifdef USE_DETERMINISTIC_MATH
215-
// TODO: return GameMath::Asin(x);
216-
return Asin(x);
209+
return gm_asinf(x);
217210
#else
218-
return Asin(x);
211+
return asinf(x);
219212
#endif
220213
}
221214

222215
// Phase 4: Origin sqrt gateway for geometry (Coord2D/Coord3D length calculations)
223216
// Must dispatch to deterministic Sqrt when enabled
224-
static WWINLINE double SqrtOrigin(double x)
225-
{
226-
#ifdef USE_DETERMINISTIC_MATH
227-
// TODO: return GameMath::Sqrt(x);
228-
return sqrt(x);
217+
// Origin wrappers: replace bare CRT math calls in GameLogic.
218+
// Each wrapper preserves the exact type (float vs double) of the vanilla CRT call.
219+
// Note: double overloads narrow to float before calling GameMath (gm_*f).
220+
// GameMath only provides float-precision functions. All call sites pass float-width
221+
// values, so the narrowing is lossless in practice.
222+
#if USE_DETERMINISTIC_MATH
223+
static WWINLINE double SqrtOrigin(double x) { return (double)gm_sqrtf((float)x); }
224+
static WWINLINE float SqrtfOrigin(float x) { return gm_sqrtf(x); }
225+
static WWINLINE double Atan2Origin(double y, double x) { return (double)gm_atan2f((float)y, (float)x); }
229226
#else
230-
return sqrt(x);
227+
static WWINLINE double SqrtOrigin(double x) { return sqrt(x); }
228+
static WWINLINE float SqrtfOrigin(float x) { return sqrtf(x); }
229+
static WWINLINE double Atan2Origin(double y, double x) { return atan2(y, x); }
231230
#endif
232-
}
233231
static WWINLINE float Sign(float val);
234232
static WWINLINE float Ceil(float val) { return ceilf(val); }
235233
static WWINLINE float Floor(float val) { return floorf(val); }

cmake/gamemath.cmake

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# Enable deterministic math only for non-VC6 builds (VC6 uses native x87 asm)
2121
# Note: Currently defaults to OFF until GameMath is available as a proper library/submodule
2222
if(NOT IS_VS6_BUILD)
23-
option(SAGE_USE_DETERMINISTIC_MATH "Use fdlibm-based deterministic math for cross-platform replay validation" OFF)
23+
option(SAGE_USE_DETERMINISTIC_MATH "Use fdlibm-based deterministic math for cross-platform replay validation" ON)
2424
else()
2525
# VC6 uses native x87 inline asm; deterministic mode not applicable
2626
set(SAGE_USE_DETERMINISTIC_MATH OFF)
@@ -31,34 +31,23 @@ if(SAGE_USE_DETERMINISTIC_MATH)
3131

3232
include(FetchContent)
3333

34-
# FetchContent declaration for GameMath library
35-
# Source: TheSuperHackers fork with deterministic math support
36-
# Can be overridden via cmake -DSAGE_GAMEMATH_GIT_REPO=<url> -DSAGE_GAMEMATH_GIT_TAG=<tag>
37-
if(NOT SAGE_GAMEMATH_GIT_REPO)
38-
set(SAGE_GAMEMATH_GIT_REPO "https://github.com/TheSuperHackers/GeneralsGameCode.git")
39-
endif()
40-
41-
if(NOT SAGE_GAMEMATH_GIT_TAG)
42-
set(SAGE_GAMEMATH_GIT_TAG "main")
43-
endif()
34+
# FORCE is required to guarantee cross-platform bit-exact determinism.
35+
# Intrinsics would use platform-specific SIMD, breaking CRC parity between architectures.
36+
set(GM_ENABLE_INTRINSICS OFF CACHE BOOL "Disable intrinsics for cross-arch determinism" FORCE)
37+
set(GM_ENABLE_TESTS OFF CACHE BOOL "Disable GameMath tests" FORCE)
4438

4539
FetchContent_Declare(
4640
gamemath
47-
GIT_REPOSITORY ${SAGE_GAMEMATH_GIT_REPO}
48-
GIT_TAG ${SAGE_GAMEMATH_GIT_TAG}
49-
SOURCE_SUBDIR "Core/GameMath" # Adjust if GameMath moves
41+
GIT_REPOSITORY https://github.com/TheSuperHackers/GameMath.git
42+
GIT_TAG 59f7ccd494f7e7c916a784ac26ef266f9f09d78d
5043
)
5144

52-
# Minimal GameMath configuration
53-
set(GAMEMATH_ENABLE_TESTS OFF CACHE BOOL "Disable GameMath tests" FORCE)
54-
set(GAMEMATH_ENABLE_EXAMPLES OFF CACHE BOOL "Disable GameMath examples" FORCE)
55-
5645
# Make GameMath available (FetchContent_MakeAvailable is idempotent)
5746
FetchContent_MakeAvailable(gamemath)
5847

59-
# Add USE_DETERMINISTIC_MATH to all compile definitions for this project
60-
# This enables conditional compilation in wwmath.h and trig wrappers
61-
add_compile_definitions(USE_DETERMINISTIC_MATH)
48+
# Ensure GameMath includes are available to ALL targets
49+
# to prevent one-definition-rule violations and ensure USE_DETERMINISTIC_MATH activates consistently.
50+
include_directories(${gamemath_SOURCE_DIR}/include)
6251

6352
message(STATUS "GameMath deterministic math enabled (fdlibm backend)")
6453
message(STATUS " Math operations will be bit-exact across platforms")

flatpak/com.fbraz3.GeneralsX.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ modules:
6565
-DFETCHCONTENT_SOURCE_DIR_SDL3_IMAGE=/run/build/generalsx/sdl3-image-src \
6666
-DFETCHCONTENT_SOURCE_DIR_OPENAL_SOFT=/run/build/generalsx/openal-soft-src \
6767
-DFETCHCONTENT_SOURCE_DIR_GAMESPY=/run/build/generalsx/gamespy-src \
68-
-DFETCHCONTENT_SOURCE_DIR_LZHL=/run/build/generalsx/lzhl-src/CompLibHeader
68+
-DFETCHCONTENT_SOURCE_DIR_LZHL=/run/build/generalsx/lzhl-src/CompLibHeader \
69+
-DFETCHCONTENT_SOURCE_DIR_GAMEMATH=/run/build/generalsx/gamemath-src
6970
- cmake --build build-flatpak --target g_generals -j "${FLATPAK_BUILDER_N_JOBS:-4}"
7071
- cmake --build build-flatpak --target sage_patch -j "${FLATPAK_BUILDER_N_JOBS:-4}" || true
7172
- install -Dm755 build-flatpak/Generals/GeneralsX /app/bin/GeneralsX
@@ -185,3 +186,7 @@ modules:
185186
url: https://github.com/TheSuperHackers/lzhl-1.0
186187
commit: dfd96e2ca64adaddb35dd4ebadd6add7d5586783
187188
dest: lzhl-src/CompLibHeader
189+
- type: git
190+
url: https://github.com/TheSuperHackers/GameMath.git
191+
commit: 59f7ccd494f7e7c916a784ac26ef266f9f09d78d
192+
dest: gamemath-src

flatpak/com.fbraz3.GeneralsXZH.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ modules:
6565
-DFETCHCONTENT_SOURCE_DIR_SDL3_IMAGE=/run/build/generalsxzh/sdl3-image-src \
6666
-DFETCHCONTENT_SOURCE_DIR_OPENAL_SOFT=/run/build/generalsxzh/openal-soft-src \
6767
-DFETCHCONTENT_SOURCE_DIR_GAMESPY=/run/build/generalsxzh/gamespy-src \
68-
-DFETCHCONTENT_SOURCE_DIR_LZHL=/run/build/generalsxzh/lzhl-src/CompLibHeader
68+
-DFETCHCONTENT_SOURCE_DIR_LZHL=/run/build/generalsxzh/lzhl-src/CompLibHeader \
69+
-DFETCHCONTENT_SOURCE_DIR_GAMEMATH=/run/build/generalsxzh/gamemath-src
6970
- cmake --build build-flatpak --target z_generals -j "${FLATPAK_BUILDER_N_JOBS:-4}"
7071
- cmake --build build-flatpak --target sage_patch -j "${FLATPAK_BUILDER_N_JOBS:-4}" || true
7172
- install -Dm755 build-flatpak/GeneralsMD/GeneralsXZH /app/bin/GeneralsXZH
@@ -188,3 +189,7 @@ modules:
188189
url: https://github.com/TheSuperHackers/lzhl-1.0
189190
commit: dfd96e2ca64adaddb35dd4ebadd6add7d5586783
190191
dest: lzhl-src/CompLibHeader
192+
- type: git
193+
url: https://github.com/TheSuperHackers/GameMath.git
194+
commit: 59f7ccd494f7e7c916a784ac26ef266f9f09d78d
195+
dest: gamemath-src

0 commit comments

Comments
 (0)