Version: Race Element 2.7.1.4
Game: Forza Horizon 6
HUDs: Shift Bar, Shift Indicator
Description
In Forza, EngineMaxRpm from the Data Out packet is the top of the in-game tachometer scale, not the rev limiter. The engine hits the limiter well below it, so the Early and Redline color states never trigger and Redline Flash never fires.
GetUpShiftPercentages() in ShiftBarOverlay.cs gates the real shift point behind a game whitelist:
Game upshiftSupportedGames = Game.RaceRoom | Game.iRacing;
Outside that set the configured percentages are applied to MaxRpm directly, which in Forza means percentages of a value the engine never reaches. ShiftIndicatorOverlay.UpdateColorDictionary() has the same pattern with an even narrower check (Game.RaceRoom only, and no divide-by-zero guard).
Measurements
Car: Fiat 124 Spider Abarth. Settings: Visible Rpm Amount 4000, Early 93.000, Redline 97.300, Redline Marker On.
From one frame with the Shift RPM HUD enabled:
- Shift RPM HUD: 7234
- Shift Bar fill: 78.4% of bar width
- Redline marker: 94.5% of bar width
Solving GetAdjustedPercentToHideRpm backwards for max rpm, with a 4000 rpm window:
- from fill:
7234 - (M - 4000) = 0.784 * 4000 gives M = 8098
- from marker:
0.973 * M - (M - 4000) = 0.945 * 4000 gives M = 8148
Both agree with the tachometer scale, which ends at 8. The bar scales correctly against EngineMaxRpm ~8100. The thresholds land at 7533 and 7881, and the engine limits below both, so the bar stays in the normal color for the whole rev range.
Suggested fix
Forza does not send a shift point. The packet only carries EngineMaxRpm, EngineIdleRpm and CurrentEngineRpm (ForzaUDP/ForzaMotorsportsData.cs), and ForzaDataProvider.cs leaves localCar.Engine.ShiftUpRpm at 0, so adding Forza to the whitelist alone would fail the upshiftRpm > 0 guard.
The limiter can be derived instead: track the peak observed CurrentEngineRpm for the current car and publish it as ShiftUpRpm. The existing branch in GetUpShiftPercentages() then works unchanged, with no new user-facing settings.
Notes on that approach:
- Reset the peak when
EngineMaxRpm changes, which already signals a car change and is what MaxRpmDetectionJob watches for.
- The peak needs a few seconds of driving to converge. Until then, falling back to the configured percentages is reasonable.
Also
ShiftIndicatorOverlay.UpdateColorDictionary() needs the same treatment. The logic is duplicated in both HUDs and could be shared.
- The Draw Upshift Data panel in
ShiftBarOverlay.Render() computes _model.MaxRpm * _config.Upshift.EarlyPercentage / 100d directly instead of calling GetUpShiftPercentages(), so on RaceRoom and iRacing the panel reports different numbers than the bar actually uses.
- CVT vehicles (for example the Polaris RZR in FH6) hold rpm roughly constant under acceleration and never approach the limiter at all. No threshold setting helps there. Mentioning it only in case it affects how a learned peak is validated.
Version: Race Element 2.7.1.4
Game: Forza Horizon 6
HUDs: Shift Bar, Shift Indicator
Description
In Forza,
EngineMaxRpmfrom the Data Out packet is the top of the in-game tachometer scale, not the rev limiter. The engine hits the limiter well below it, so the Early and Redline color states never trigger and Redline Flash never fires.GetUpShiftPercentages()inShiftBarOverlay.csgates the real shift point behind a game whitelist:Outside that set the configured percentages are applied to
MaxRpmdirectly, which in Forza means percentages of a value the engine never reaches.ShiftIndicatorOverlay.UpdateColorDictionary()has the same pattern with an even narrower check (Game.RaceRoomonly, and no divide-by-zero guard).Measurements
Car: Fiat 124 Spider Abarth. Settings: Visible Rpm Amount 4000, Early 93.000, Redline 97.300, Redline Marker On.
From one frame with the Shift RPM HUD enabled:
Solving
GetAdjustedPercentToHideRpmbackwards for max rpm, with a 4000 rpm window:7234 - (M - 4000) = 0.784 * 4000gives M = 80980.973 * M - (M - 4000) = 0.945 * 4000gives M = 8148Both agree with the tachometer scale, which ends at 8. The bar scales correctly against
EngineMaxRpm~8100. The thresholds land at 7533 and 7881, and the engine limits below both, so the bar stays in the normal color for the whole rev range.Suggested fix
Forza does not send a shift point. The packet only carries
EngineMaxRpm,EngineIdleRpmandCurrentEngineRpm(ForzaUDP/ForzaMotorsportsData.cs), andForzaDataProvider.csleaveslocalCar.Engine.ShiftUpRpmat 0, so adding Forza to the whitelist alone would fail theupshiftRpm > 0guard.The limiter can be derived instead: track the peak observed
CurrentEngineRpmfor the current car and publish it asShiftUpRpm. The existing branch inGetUpShiftPercentages()then works unchanged, with no new user-facing settings.Notes on that approach:
EngineMaxRpmchanges, which already signals a car change and is whatMaxRpmDetectionJobwatches for.Also
ShiftIndicatorOverlay.UpdateColorDictionary()needs the same treatment. The logic is duplicated in both HUDs and could be shared.ShiftBarOverlay.Render()computes_model.MaxRpm * _config.Upshift.EarlyPercentage / 100ddirectly instead of callingGetUpShiftPercentages(), so on RaceRoom and iRacing the panel reports different numbers than the bar actually uses.