@@ -28,6 +28,9 @@ native ReportTrailerUpdate(vehicleid, trailerid);
2828# define VEHICLE_KEYS_BINDING_BLINKER_LEFT KEY_LOOK_LEFT
2929# define VEHICLE_KEYS_BINDING_GRAVITY KEY_ANALOG_UP
3030
31+ // Warn the player once every X milliseconds about their damage being disabled due to High FPS.
32+ new const kHighFpsWarningTimeMs = 60 * 1000 ;
33+
3134// Polygon that encloses the area of Las Venturas, on the insides of the highway.
3235new const Float: kLasVenturasAreaPolygon[] = {
3336 1353 .59 , 832 .49 ,
@@ -57,6 +60,11 @@ new STREAMER_TAG_AREA: g_areaLasVenturas;
5760// Time (in milliseconds) until when damage issued by a particular player should be disabled.
5861new g_damageDisabledExpirationTime[MAX_PLAYERS] = { 0 , ... };
5962
63+ // Counter for number of subsequent High FPS detections, and time at which the last warning was
64+ // issued to a particular player, to let them know damage was disabled.
65+ new g_highFramerateCounter[MAX_PLAYERS] = { 0 , ... };
66+ new g_highFramerateWarningTime[MAX_PLAYERS] = { 0 , ... };
67+
6068// Boolean that indicates whether a particular player is currently in Las Venturas.
6169new bool: g_inLasVenturas[MAX_PLAYERS] = { false , ... };
6270
@@ -139,6 +147,8 @@ InitializeAreas() {
139147
140148public OnPlayerConnect (playerid) {
141149 g_aimbotSuspicionCount[playerid] = 0 ;
150+ g_highFramerateCounter[playerid] = 0 ;
151+ g_highFramerateWarningTime[playerid] = 0 ;
142152 g_lastTakenDamageIssuerId[playerid] = - 1 ;
143153 g_lastTakenDamageTime[playerid] = 0 ;
144154 g_sprayTagStartTime[playerid] = 0 ;
@@ -182,6 +192,50 @@ public OnPlayerDisconnect(playerid, reason) {
182192 return PlayerEvents (playerid)- > onPlayerDisconnect (reason);
183193}
184194
195+ // Processes framerates of all connected human players and marks them as regular or high FPS. When
196+ // they've been marked as high FPS for a certain number of seconds, their damage might be disabled.
197+ ProcessHighFrameratePlayers () {
198+ new const currentTime = GetTickCount ();
199+
200+ for (new playerId = 0 ; playerId < GetPlayerPoolSize (); ++ playerId) {
201+ if (! IsPlayerConnected (playerId) || IsPlayerNPC (playerId))
202+ continue ; // skip unconnected players and NPCs
203+
204+ // Reset and bail when the |playerId|'s framerate is within acceptable bounds
205+ if (PlayerManager- > framesPerSecond (playerId) < g_abuseHighFramerateDamageThreshold) {
206+ g_highFramerateCounter[playerId] = 0 ;
207+ continue ;
208+ }
209+
210+ if (++ g_highFramerateCounter[playerId] < g_abuseHighFramerateDamageSampleSec)
211+ continue ; // not enough subsequent samples yet
212+
213+ // (1) Disable the |playerId|'s damage for the configured amount of seconds.
214+ g_damageDisabledExpirationTime[playerId] = max (
215+ g_damageDisabledExpirationTime[playerId],
216+ currentTime + g_abuseHighFramerateDamageIgnoredSec * 1000 );
217+
218+ // (2) Share a warning with the |playerId|, but at most once per |kHighFpsWarningTimeMs|.
219+ if (g_highFramerateWarningTime[playerId] == 0 ||
220+ (currentTime - g_highFramerateWarningTime[playerId]) > kHighFpsWarningTimeMs) {
221+ g_highFramerateWarningTime[playerId] = currentTime;
222+
223+ new warningMessage[128 ];
224+ format (
225+ warningMessage, sizeof (warningMessage),
226+ " * Your ability to inflict damage has been disabled because your FPS topped %d ." ,
227+ g_abuseHighFramerateDamageThreshold);
228+
229+ SendClientMessage (playerId, Color: : Error, warningMessage);
230+
231+ // (3) Log this to the console as well, so that we can inspect how often this happens.
232+ printf (" [highfps] %s (Id:%d ) has been detected as a High FPS player with an FPS of %d ." ,
233+ Player (playerId)- > nicknameString (), playerId,
234+ PlayerManager- > framesPerSecond (playerId));
235+ }
236+ }
237+ }
238+
185239// Returns whether vehicle keys are available to the |playerid|, based on their current state.
186240bool: AreVehicleKeysAvailable (playerid) {
187241 return ! PlayerSyncedData (playerid)- > hasMinigameName () &&
@@ -468,8 +522,9 @@ public OnPlayerStateChange(playerid, newstate, oldstate) {
468522 // to avoid players from abusing vehicle bugs to give them an advantage in a fight.
469523 if ((oldstate == PLAYER_STATE_DRIVER || oldstate == PLAYER_STATE_PASSENGER)
470524 && g_abuseFakeCarEntryPreventionExitMs > 0 ) {
471- g_damageDisabledExpirationTime[playerid] =
472- GetTickCount () + g_abuseFakeCarEntryPreventionExitMs;
525+ g_damageDisabledExpirationTime[playerid] = max (
526+ g_damageDisabledExpirationTime[playerid],
527+ GetTickCount () + g_abuseFakeCarEntryPreventionExitMs);
473528 }
474529
475530 return LegacyPlayerStateChange (playerid, newstate, oldstate);
@@ -656,8 +711,9 @@ public OnPlayerUpdate(playerid) {
656711 switch (animationIndex) {
657712 case 1043 /* CAR_OPEN_LHS */ , 1044 /* CAR_OPEN_RHS */ ,
658713 1026 /* CAR_GETIN_LHS */ , 1027 /* CAR_GETIN_RHS */ : {
659- g_damageDisabledExpirationTime[playerid] =
660- GetTickCount () + g_abuseFakeCarEntryPreventionEnterMs;
714+ g_damageDisabledExpirationTime[playerid] = max (
715+ g_damageDisabledExpirationTime[playerid],
716+ GetTickCount () + g_abuseFakeCarEntryPreventionEnterMs);
661717 }
662718 }
663719 }
0 commit comments