Skip to content

Commit 0cdbc81

Browse files
authored
feat(math): integrate deterministic GameMath (#176)
* 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. * Enforce cross-platform math determinism (GameMath) & Fix Linux build * Fix remaining non-deterministic native math calls in Core libraries This commit addresses remaining naked CRT calls (ceilf, floorf, sqrt) in BaseType.h, Point.h, INI.cpp, and AIPathfind.cpp that were previously missed, enforcing determinism via WWMath and fast_float wrappers to prevent cross-platform replay desyncs. * docs(devblog): sanitize user path in dev blog * fix(audio): resolve FPU precision state leaks and disable retail macros - Implement ScopedFPUGuard to restore FPU precision mode upon scope exit in MiniAudio and OpenAL audio managers. - Disable legacy RETAIL_COMPATIBLE_* macros (CRC, PATHFINDING, AIGroup, Networking, etc.) in GameDefines.h to avoid dynamic RNG/memory inconsistencies. * ci(replay-tests): fetch assets/replays from gitlab with caching Add GitLab cloning with GitHub fallback for replays. Implement branch matching for assets and replays repositories. Enable caching for the replay files repository to optimize CI runs. * chore(engine): enable retail compatibility modes and update resolution Enable retail compatibility macros (scripted camera, CRC, pathfinding, allocation, AI group) and set default display resolution to 1024x768. * disable all retail compatibility
1 parent d3ee2cc commit 0cdbc81

97 files changed

Lines changed: 517 additions & 393 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/replay-tests.yml

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -159,23 +159,24 @@ jobs:
159159
uses: actions/cache@v5
160160
with:
161161
path: /tmp/cached-assets
162-
key: game-assets-cache-v1
163-
164-
# - name: Checkout game assets repo
165-
# if: steps.cache-assets.outputs.cache-hit != 'true'
166-
# uses: actions/checkout@v7
167-
# with:
168-
# repository: fbraz3/generalsx-test-files
169-
# path: ci-assets-repo
170-
# lfs: true
162+
key: game-assets-cache-v2
171163

172164
- name: Clone game assets repo (GitLab with GitHub fallback)
173165
if: steps.cache-assets.outputs.cache-hit != 'true'
174166
run: |
167+
# GeneralsX @build fbraz3 24/06/2026 Support checking out current branch if present in assets/replays repo.
168+
TARGET_BRANCH="${{ github.head_ref || github.ref_name }}"
175169
clone_success=true
176170
git clone https://gitlab.com/fbraz3/generalsx-test-files.git ci-assets-repo || clone_success=false
177171
if [ "$clone_success" = true ]; then
178172
cd ci-assets-repo
173+
if [ "$TARGET_BRANCH" != "main" ] && [ -n "$TARGET_BRANCH" ]; then
174+
if git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "refs/heads/$TARGET_BRANCH"; then
175+
echo "Branch $TARGET_BRANCH exists on GitLab. Checking out..."
176+
git fetch origin "$TARGET_BRANCH"
177+
git checkout "$TARGET_BRANCH"
178+
fi
179+
fi
179180
git lfs install
180181
if git lfs pull; then
181182
echo "Successfully cloned and pulled LFS from GitLab."
@@ -192,6 +193,13 @@ jobs:
192193
if [ "$clone_success" = false ]; then
193194
git clone https://github.com/fbraz3/generalsx-test-files.git ci-assets-repo
194195
cd ci-assets-repo
196+
if [ "$TARGET_BRANCH" != "main" ] && [ -n "$TARGET_BRANCH" ]; then
197+
if git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "refs/heads/$TARGET_BRANCH"; then
198+
echo "Branch $TARGET_BRANCH exists on GitHub. Checking out..."
199+
git fetch origin "$TARGET_BRANCH"
200+
git checkout "$TARGET_BRANCH"
201+
fi
202+
fi
195203
git lfs install
196204
git lfs pull
197205
fi
@@ -223,11 +231,46 @@ jobs:
223231
# 5. Check out replay repo and install maps + platform-specific replays
224232
# Repo structure: GeneralsXZH/Maps/<MapFolder>/ GeneralsXZH/Replays/<platform>_*.rep
225233
# -----------------------------------------------------------------------
226-
- name: Checkout replay files repo
227-
uses: actions/checkout@v6
234+
- name: Cache Replay Files
235+
id: cache-replays
236+
uses: actions/cache@v5
228237
with:
229-
repository: fbraz3/GeneralsXReplays
230238
path: ci-replays-repo
239+
key: replay-files-cache-v1
240+
241+
- name: Clone replay files repo (GitLab with GitHub fallback)
242+
if: steps.cache-replays.outputs.cache-hit != 'true'
243+
run: |
244+
# GeneralsX @build fbraz3 24/06/2026 Clone replays from GitLab with GitHub fallback and support checking out current branch.
245+
TARGET_BRANCH="${{ github.head_ref || github.ref_name }}"
246+
clone_success=true
247+
git clone https://gitlab.com/fbraz3/generalsx-replays.git ci-replays-repo || clone_success=false
248+
if [ "$clone_success" = true ]; then
249+
cd ci-replays-repo
250+
if [ "$TARGET_BRANCH" != "main" ] && [ -n "$TARGET_BRANCH" ]; then
251+
if git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "refs/heads/$TARGET_BRANCH"; then
252+
echo "Branch $TARGET_BRANCH exists on GitLab. Checking out..."
253+
git fetch origin "$TARGET_BRANCH"
254+
git checkout "$TARGET_BRANCH"
255+
fi
256+
fi
257+
echo "Successfully cloned replays from GitLab."
258+
else
259+
echo "GitLab clone failed. Cleaning up and falling back to GitHub..."
260+
rm -rf ci-replays-repo
261+
fi
262+
if [ "$clone_success" = false ]; then
263+
git clone https://github.com/fbraz3/GeneralsXReplays.git ci-replays-repo
264+
cd ci-replays-repo
265+
if [ "$TARGET_BRANCH" != "main" ] && [ -n "$TARGET_BRANCH" ]; then
266+
if git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "refs/heads/$TARGET_BRANCH"; then
267+
echo "Branch $TARGET_BRANCH exists on GitHub. Checking out..."
268+
git fetch origin "$TARGET_BRANCH"
269+
git checkout "$TARGET_BRANCH"
270+
fi
271+
fi
272+
echo "Successfully cloned replays from GitHub."
273+
fi
231274
232275
- name: Set up user data directories (Linux)
233276
if: inputs.platform == 'linux' || inputs.platform == 'linux-flatpak'
@@ -244,12 +287,12 @@ jobs:
244287
ls -la "$MAPS_DIR/"
245288
fi
246289
247-
# Install platform-specific replays
248-
REPLAY_COUNT=$(ls "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/linux_*.rep 2>/dev/null | wc -l)
290+
# Install all replays
291+
REPLAY_COUNT=$(ls "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/*.rep 2>/dev/null | wc -l)
249292
if [ "$REPLAY_COUNT" -eq 0 ]; then
250-
echo "WARNING: No linux_*.rep files found in replay repo"
293+
echo "WARNING: No .rep files found in replay repo"
251294
else
252-
cp "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/linux_*.rep "$REPLAYS_DIR/"
295+
cp "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/*.rep "$REPLAYS_DIR/"
253296
echo "Replays installed ($REPLAY_COUNT files):"
254297
ls -la "$REPLAYS_DIR/"
255298
fi
@@ -279,12 +322,12 @@ jobs:
279322
ls -la "$MAPS_DIR/"
280323
fi
281324
282-
# Install platform-specific replays
283-
REPLAY_COUNT=$(ls "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/macos_*.rep 2>/dev/null | wc -l)
325+
# Install all replays
326+
REPLAY_COUNT=$(ls "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/*.rep 2>/dev/null | wc -l)
284327
if [ "$REPLAY_COUNT" -eq 0 ]; then
285-
echo "WARNING: No macos_*.rep files found in replay repo"
328+
echo "WARNING: No .rep files found in replay repo"
286329
else
287-
cp "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/macos_*.rep "$REPLAYS_DIR/"
330+
cp "$GITHUB_WORKSPACE/ci-replays-repo/GeneralsXZH/Replays"/*.rep "$REPLAYS_DIR/"
288331
echo "Replays installed ($REPLAY_COUNT files):"
289332
ls -la "$REPLAYS_DIR/"
290333
fi

Core/GameEngine/Include/Common/GameDefines.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@
8080
#endif
8181

8282
#ifndef PRESERVE_RETAIL_SCRIPTED_CAMERA
83-
#define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04
83+
#define PRESERVE_RETAIL_SCRIPTED_CAMERA (0) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04
8484
#endif
8585

8686
#ifndef RETAIL_COMPATIBLE_CRC
87-
#define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
87+
#define RETAIL_COMPATIBLE_CRC (0) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
8888
#endif
8989

9090
#ifndef RETAIL_COMPATIBLE_XFER_SAVE
@@ -93,16 +93,16 @@
9393

9494
// This is here to easily toggle between the retail compatible with fixed pathfinding fallback and pure fixed pathfinding mode
9595
#ifndef RETAIL_COMPATIBLE_PATHFINDING
96-
#define RETAIL_COMPATIBLE_PATHFINDING (1)
96+
#define RETAIL_COMPATIBLE_PATHFINDING (0)
9797
#endif
9898

9999
// This is here to easily toggle between the retail compatible pathfinding memory allocation and the new static allocated data mode
100100
#ifndef RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION
101-
#define RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION (1)
101+
#define RETAIL_COMPATIBLE_PATHFINDING_ALLOCATION (0)
102102
#endif
103103

104104
#ifndef RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM
105-
#define RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM (1) // Use the original circle fill algorithm, which is more efficient but less accurate
105+
#define RETAIL_COMPATIBLE_CIRCLE_FILL_ALGORITHM (0) // Use the original circle fill algorithm, which is more efficient but less accurate
106106
#endif
107107

108108
// Disable non retail fixes in the networking, such as putting more data per UDP packet
@@ -115,7 +115,7 @@
115115
// but put them behind this macro.
116116

117117
#ifndef RETAIL_COMPATIBLE_AIGROUP
118-
#define RETAIL_COMPATIBLE_AIGROUP (1) // AIGroup logic is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
118+
#define RETAIL_COMPATIBLE_AIGROUP (0) // AIGroup logic is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
119119
#endif
120120

121121
#ifndef ENABLE_GAMETEXT_SUBSTITUTES
@@ -174,5 +174,5 @@
174174

175175
#define MIN_DISPLAY_BIT_DEPTH 16
176176
#define DEFAULT_DISPLAY_BIT_DEPTH 32
177-
#define DEFAULT_DISPLAY_WIDTH 800 // The standard resolution this game was designed for
178-
#define DEFAULT_DISPLAY_HEIGHT 600 // The standard resolution this game was designed for
177+
#define DEFAULT_DISPLAY_WIDTH 1024 // The standard resolution this game was designed for
178+
#define DEFAULT_DISPLAY_HEIGHT 768 // The standard resolution this game was designed for

Core/GameEngine/Source/Common/INI/INI.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
3232
#define DEFINE_DEATH_NAMES
3333

34+
#include "WWMath/wwmath.h"
3435
#include "Common/INI.h"
3536
#include "Common/INIException.h"
3637

@@ -1866,15 +1867,15 @@ void INI::parseDurationReal( INI *ini, void * /*instance*/, void *store, const v
18661867
void INI::parseDurationUnsignedInt( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
18671868
{
18681869
UnsignedInt val = scanUnsignedInt(ini->getNextToken());
1869-
*(UnsignedInt *)store = (UnsignedInt)ceilf(ConvertDurationFromMsecsToFrames((Real)val));
1870+
*(UnsignedInt *)store = (UnsignedInt)WWMath::Ceil(ConvertDurationFromMsecsToFrames((Real)val));
18701871
}
18711872

18721873
// ------------------------------------------------------------------------------------------------
18731874
// parse a duration in msec and convert to duration in integral number of frames, (unsignedshort) rounding UP
18741875
void INI::parseDurationUnsignedShort( INI *ini, void * /*instance*/, void *store, const void* /*userData*/ )
18751876
{
18761877
UnsignedInt val = scanUnsignedInt(ini->getNextToken());
1877-
*(UnsignedShort *)store = (UnsignedShort)ceilf(ConvertDurationFromMsecsToFrames((Real)val));
1878+
*(UnsignedShort *)store = (UnsignedShort)WWMath::Ceil(ConvertDurationFromMsecsToFrames((Real)val));
18781879
}
18791880

18801881
//-------------------------------------------------------------------------------------------------

Core/GameEngine/Source/GameClient/MessageStream/PlaceEventTranslator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ GameMessageDisposition PlaceEventTranslator::translateGameMessage(const GameMess
339339
Int x, y;
340340
x = mouse.x - start.x;
341341
y = mouse.y - start.y;
342-
if( sqrt( (x * x) + (y * y) ) >= PLACEMENT_DRAG_THRESHOLD_DIST )
342+
if( WWMath::SqrtOrigin( (x * x) + (y * y) ) >= PLACEMENT_DRAG_THRESHOLD_DIST )
343343
{
344344

345345
TheInGameUI->setPlacementEnd(&mouse);

Core/GameEngine/Source/GameLogic/AI/AIPathfind.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
// Author: Michael S. Booth, October 2001
2828
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
2929

30+
#include "WWMath/wwmath.h"
3031
#include "GameLogic/AIPathfind.h"
3132

3233
#include "Common/PerfTimer.h"
@@ -921,7 +922,7 @@ void Path::computePointOnPath(
921922
// compute distance of point from this path segment
922923
Real toDistSqr = sqr(toPos.x) + sqr(toPos.y);
923924
Real offsetDistSq = toDistSqr - sqr(alongPathDist);
924-
Real offsetDist = (offsetDistSq <= 0.0) ? 0.0 : sqrt(offsetDistSq);
925+
Real offsetDist = (offsetDistSq <= 0.0) ? 0.0 : WWMath::SqrtOrigin(offsetDistSq);
925926

926927
// If we are basically on the path, return the next path node as the movement goal.
927928
// However, the farther off the path we get, the movement goal becomes closer to our
@@ -2070,7 +2071,7 @@ UnsignedInt PathfindCell::costToGoal( PathfindCell *goal )
20702071
Int dy = m_info->m_pos.y - goal->getYIndex();
20712072
#define NO_REAL_DIST
20722073
#ifdef REAL_DIST
2073-
Int cost = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
2074+
Int cost = COST_ORTHOGONAL*WWMath::SqrtOrigin(dx*dx + dy*dy);
20742075
#else
20752076
if (dx<0) dx = -dx;
20762077
if (dy<0) dy = -dy;
@@ -2096,7 +2097,7 @@ UnsignedInt PathfindCell::costToHierGoal( PathfindCell *goal )
20962097
}
20972098
Int dx = m_info->m_pos.x - goal->getXIndex();
20982099
Int dy = m_info->m_pos.y - goal->getYIndex();
2099-
Int cost = REAL_TO_INT_FLOOR(COST_ORTHOGONAL*sqrt(dx*dx + dy*dy) + 0.5f);
2100+
Int cost = REAL_TO_INT_FLOOR(COST_ORTHOGONAL*WWMath::SqrtOrigin(dx*dx + dy*dy) + 0.5f);
21002101
return cost;
21012102
}
21022103

@@ -6444,7 +6445,7 @@ Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell *
64446445
} else {
64456446
dx = newCellCoord.x - goalCell->getXIndex();
64466447
dy = newCellCoord.y - goalCell->getYIndex();
6447-
costRemaining = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
6448+
costRemaining = COST_ORTHOGONAL*WWMath::SqrtOrigin(dx*dx + dy*dy);
64486449
costRemaining -= attackDistance/2;
64496450
if (costRemaining<0)
64506451
costRemaining=0;
@@ -6768,7 +6769,7 @@ Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSe
67686769
dx = from->x - to->x;
67696770
dy = from->y - to->y;
67706771

6771-
Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
6772+
Int count = WWMath::SqrtOrigin(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
67726773
if (count<2) count = 2;
67736774
Int i;
67746775
color.green = 0;
@@ -7459,7 +7460,7 @@ Path *Pathfinder::findGroundPath( const Coord3D *from,
74597460
dx = from->x - to->x;
74607461
dy = from->y - to->y;
74617462

7462-
Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
7463+
Int count = WWMath::SqrtOrigin(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
74637464
if (count<2) count = 2;
74647465
Int i;
74657466
color.green = 0;
@@ -8163,7 +8164,7 @@ Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSu
81638164
dx = from->x - to->x;
81648165
dy = from->y - to->y;
81658166

8166-
Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
8167+
Int count = WWMath::SqrtOrigin(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
81678168
if (count<2) count = 2;
81688169
Int i;
81698170
color.green = 0;
@@ -11216,7 +11217,7 @@ Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotor
1121611217
farthestDistanceSqr = distSqr;
1121711218
if (cellCount > MAX_CELLS) {
1121811219
#ifdef INTENSE_DEBUG
11219-
DEBUG_LOG(("Took intermediate path, dist %f, goal dist %f", sqrt(farthestDistanceSqr), repulsorRadius));
11220+
DEBUG_LOG(("Took intermediate path, dist %f, goal dist %f", WWMath::SqrtOrigin(farthestDistanceSqr), repulsorRadius));
1122011221
#endif
1122111222
ok = true; // Already a big search, just take this one.
1122211223
}

Core/GameEngineDevice/Source/MiniAudioDevice/MiniAudioManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959

6060
#include "GameLogic/GameLogic.h"
6161
#include "GameLogic/TerrainLogic.h"
62+
// GeneralsX @bugfix fbraz3 24/06/2026 Save and restore FPU precision mode when calling audio manager entrypoints.
63+
#include "GameLogic/FPUControl.h"
6264

6365
#include "Common/file.h"
6466
#include "VideoDevice/FFmpeg/FFmpegFile.h"
@@ -108,6 +110,7 @@ MiniAudioManager::~MiniAudioManager()
108110
#if defined(_DEBUG) || defined(_INTERNAL)
109111
AudioHandle MiniAudioManager::addAudioEvent(const AudioEventRTS *eventToAdd)
110112
{
113+
ScopedFPUGuard fpuGuard;
111114
if (TheGlobalData->m_preloadReport) {
112115
if (!eventToAdd->getEventName().isEmpty()) {
113116
m_allEventsLoaded.insert(eventToAdd->getEventName());
@@ -176,6 +179,7 @@ void MiniAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FILE
176179
//-------------------------------------------------------------------------------------------------
177180
void MiniAudioManager::init()
178181
{
182+
ScopedFPUGuard fpuGuard;
179183
AudioManager::init();
180184
#ifdef INTENSE_DEBUG
181185
return;
@@ -204,6 +208,7 @@ void MiniAudioManager::reset()
204208
//-------------------------------------------------------------------------------------------------
205209
void MiniAudioManager::update()
206210
{
211+
ScopedFPUGuard fpuGuard;
207212
AudioManager::update();
208213
setDeviceListenerPosition();
209214
processRequestList();

Core/GameEngineDevice/Source/OpenALAudioDevice/OpenALAudioManager.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363

6464
#include "GameLogic/GameLogic.h"
6565
#include "GameLogic/TerrainLogic.h"
66+
// GeneralsX @bugfix fbraz3 24/06/2026 Save and restore FPU precision mode when calling audio manager entrypoints.
67+
#include "GameLogic/FPUControl.h"
6668

6769
#include "Common/file.h"
6870

@@ -126,6 +128,7 @@ OpenALAudioManager::~OpenALAudioManager()
126128
#if defined(_DEBUG) || defined(_INTERNAL)
127129
AudioHandle OpenALAudioManager::addAudioEvent(const AudioEventRTS* eventToAdd)
128130
{
131+
ScopedFPUGuard fpuGuard;
129132
if (TheGlobalData->m_preloadReport) {
130133
if (!eventToAdd->getEventName().isEmpty()) {
131134
m_allEventsLoaded.insert(eventToAdd->getEventName());
@@ -490,6 +493,7 @@ ALenum OpenALAudioManager::getALFormat(uint8_t channels, uint8_t bitsPerSample)
490493
//-------------------------------------------------------------------------------------------------
491494
void OpenALAudioManager::init()
492495
{
496+
ScopedFPUGuard fpuGuard;
493497
AudioManager::init();
494498
#ifdef INTENSE_DEBUG
495499
DEBUG_LOG(("Sound has temporarily been disabled in debug builds only. jkmcd\n"));
@@ -528,6 +532,7 @@ void OpenALAudioManager::reset()
528532
//-------------------------------------------------------------------------------------------------
529533
void OpenALAudioManager::update()
530534
{
535+
ScopedFPUGuard fpuGuard;
531536
AudioManager::update();
532537
setDeviceListenerPosition();
533538
processRequestList();

Core/Libraries/Include/Lib/BaseType.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,9 @@ __forceinline float fast_float_ceil(float f)
223223
#define INT_TO_REAL(x) ((Real)(x))
224224

225225
// once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj)
226-
#if RTS_GENERALS /*&& RETAIL_COMPATIBLE_CRC*/
227-
#define REAL_TO_INT_CEIL(x) (fast_float2long_round(ceilf(x)))
228-
#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(floorf(x)))
229-
#else
226+
// GeneralsX @feature fbraz 03/05/2026 Use deterministic fast_float functions universally to ensure cross-platform replay determinism.
230227
#define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x)))
231228
#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x)))
232-
#endif
233229

234230
#define FAST_REAL_TRUNC(x) fast_float_trunc(x)
235231
#define FAST_REAL_CEIL(x) fast_float_ceil(x)

0 commit comments

Comments
 (0)