Client speed hack

I’ve been reading CharacterMovementComponent.cpp for a while (nice read, btw) but haven’t found anything that would prevent client from pretending that it moves with increased time speed compared to real time. For example, client could always use x2 delta time (thus, resulting in x2 move distances).

Am I wrong and there is some code that protects from this?

The server should correct for any illegal movement causing them to bounce around. This isn’t necessarily a good thing though. Unreal Tournament implements it’s own version of speed hack detection where it pauses player updates until the server catches up to where it thinks the client is (at least that is my understanding). Paragon just recently encountered a version of this exploit and is doing work to block and detect this. I’ll add those folks that addressed this to this ticket so they can add any extra info

Your assessment is correct.

As Joe says, UT implements speedhack detection and prevention, and Paragon very recently (a few days ago) brought that over. We are currently iterating on it (testing against real-world cheat programs) and will integrate it over to mainline once we’ve cut down on the false positives.

Current implementation compares the time delta of timestamps reported through ServerMove RPCs to time passed on the server and makes the client “pay back” any significant deviation by forcing future MoveAutonomous calls to have near-zero tick time (until the server has caught up).

Speed hack detection and protection is available at the engine level in CharacterMovementComponent (although disabled by default) as of CL2907678 in //UE4/Main, which is available in the master branch on GitHub right now. This should make it into 4.12 release.

The following is the changelist description, containing a short description and the options for enabling it:

Character Movement Time Discrepancy Protection

  • Added time discrepancy detection and resolution logic to CharacterMovementComponent, meant for protecting networked games against client time manipulation (speed hacks)

  • Detection works by the server comparing client ServerMove RPC timestamp deltas against the known server authoritative time delta since last ServerMove was received over time, and when that discrepancy reaches a configurable threshold we trigger detection and optionally resolution code. Resolution works by overriding future client ServerMove RPCs delta times to server time and having a portion of those move deltas be used for “paying back” the stolen time (the total time discrepancy detected) until the time difference has equalized.

  • Disabled by default, options live in GameNetworkManager and are the following:

  • bMovementTimeDiscrepancyDetection: Enable detection

  • bMovementTimeDiscrepancyResolution: Enable resolution

  • MovementTimeDiscrepancyMaxTimeMargin: Maximum time client can be ahead before triggering detection/resolution

  • MovementTimeDiscrepancyMinTimeMargin: Maximum time client can be behind

  • MovementTimeDiscrepancyResolutionRate: Rate at which to “pay back” stolen time during resolution, defaults to 100%

  • MovementTimeDiscrepancyDriftAllowance: Accepted drift in clocks between client and server as a percent per second allowed (to help burst packet loss/performance hitches from triggering false positives

  • bMovementTimeDiscrepancyForceCorrectionsDuringResolution: Whether to force client updates during resolution, needed for projects that have lenient movement error tolerances or ClientAuthorativePosition enabled

  • Added ClientErrorUpdateRateLimit GameNetworkManager parameter as a way of limiting client error update rates. Previously AGameNetworkManager::WithinUpdateDelayBounds only dynamically triggered based on the ratio between CLIENTADJUSTUPDATECOST and a player’s CurrentNetSpeed, now it can (optionally) be made more explicit. When enabled, this reduces burden on the server of sending enormous number of updates to clients with extremely high FPS (300+ FPS)

  • Added the above settings to BaseGame.ini, cleaned up deprecated GameNetworkManager parameters

  • Detection and resolution logic lives within UCharacterMovementComponent::ProcessClientTimeStampForTimeDiscrepancy(). This triggers virtual UCharacterMovementComponent::OnTimeDiscrepancyDetected() for project-specific alerts/handling.

  • Added several new variables to FNetworkPredictionData_Server_Character for time discrepancy purposes, and cleaned up existing comments.

  • Renamed MaxResponseTime in prediction data to MaxMoveDeltaTime to reflect what that variable has actually been doing for a few years now (previous comments/naming confused it with MAXCLIENTUPDATEINTERVAL), deprecated MaxResponseTime so projects have an easier transition, added GameNetworkManager::MaxMoveDeltaTime config variable for setting the default value per-project.

  • Made several pre-existing GameNetworkManager variables configurable in ini files and exposed in BaseGame.ini for better discoverability

  • Did a fix-up pass on global and actor time dilation usage in CharacterMovementComponent - there were several instances where AActor::CustomTimeDilation was being double-applied or misused, tested both global slomo and CustomTimeDilation thoroughly to ensure movement code and time discrepancy detection/resolution handles it correctly

  • Changed ServerMoveOld to use same DeltaTime calculation/tracking as ServerMove RPC does so that they’re consistent and so time discrepancy detection works with both.

  • Added virtual UCharacterMovementComponent::OnClientTimeStampResetDetected() function so that if projects have specific need for knowing when client time stamps occur there’s an easy way to access that information. Use case for this would be wanting to track how often clients attempt to reset their time stamps, another possible avenue for exploitation.

  • Added p.DebugTimeDiscrepancy cvar for enabling log spew of time discrepancy state on servers

Let me know if you have any questions or run into issues enabling it for your project. It should work out of the box as soon as you enable detection and resolution in DefaultGame.ini, see BaseGame.ini [/Script/Engine.GameNetworkManager] section. Default DraftAllowance is set at 0% so that there’s no forgiveness for initial testing purposes, but for a live game I would recommend bumping that up to cut back on false positives.