OnPlayerTakeDamage reliability issue
#1

In light of the upcoming update I'm sure many server owners and developers are looking to create anti-cheat systems by combining the OnPlayerTakeDamage and OnPlayerWeaponShot callbacks. However, after conducting some testing I've concluded that there seem to be some reliability issues with OnPlayerTakeDamage. The damage OnPlayerTakeDamage reports seems to be different from the actual damage the player takes.

For clarification so that everyone understands what is what:

The amounts shown as "Health before" are:
Код:
GetPlayerHealth(playerid, fHealth);
The amounts shown as "Health after" are:
Quote:

format(string, sizeof(string), "(OnPlayerTakeDamage) Health before: %.1f (%f) | Health after: %.1f (%f)", fHealth, fHealth, fHealth-amount, fHealth-amount);

So basically health minus damage taken (amount) as reported by OnPlayerTakeDamage.


The amounts shown as "Server-side health" and "Client-side health" are:
Quote:

format(string, sizeof(string), "ID: %d | Server-side health: %.1f (%f) | Client-side health: %.1f (%f)", playerid, ValidHealth[playerid], ValidHealth[playerid], fHealth, fHealth);

This is called a couple of seconds after OnPlayerTakeDamage. ValidHealth is the same as "Health after", so health minus damage taken (amount) as reported by OnPlayerTakeDamage.


Here are the tests with several different weapons (all of these are a single shot/punch):

You will notice that the damages reported vary per OnPlayerTakeDamage and GetPlayerHealth.

Punch
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 1.3 (1.320000) | WeaponID: 0 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 98.6 (98.680000)

Quote:

ID: 1 | Server-side health: 98.6 (98.680000) | Client-side health: 98.0 (98.000000)

9mm
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 8.2 (8.250000) | WeaponID: 22 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 91.7 (91.750000)

Quote:

ID: 1 | Server-side health: 91.7 (91.750000) | Client-side health: 91.0 (91.000000)

MP5
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 8.2 (8.250000) | WeaponID: 29 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 91.7 (91.750000)

Quote:

(ID: 1 | Server-side health: 91.7 (91.750000) | Client-side health: 91.0 (91.000000)

Shotgun
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 49.5 (49.500003) | WeaponID: 25 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 50.4 (50.499996)

Quote:

ID: 1 | Server-side health: 50.4 (50.499996) | Client-side health: 50.0 (50.000000)

Deagle
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 46.2 (46.200000) | WeaponID: 24 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 53.7 (53.799999)

Quote:

ID: 1 | Server-side health: 53.7 (53.799999) | Client-side health: 53.0 (53.000000)

SPAS-12
Quote:

ID: 1 | IssuerID: 0 | Amount: 39.6 (39.600002) | WeaponID: 27 | Bodypart: 3
Health before: 100.0 (100.000000) | Health after: 60.3 (60.399997)

Quote:

ID: 1 | Server-side health: 60.3 (60.399997) | Client-side health: 60.0 (60.000000)

Sniper
Quote:

(OnPlayerTakeDamage) ID: 1 | IssuerID: 0 | Amount: 41.2 (41.250000) | WeaponID: 34 | Bodypart: 3
(OnPlayerTakeDamage) Health before: 100.0 (100.000000) | Health after: 58.7 (58.750000)

Quote:

ID: 1 | Server-side health: 58.7 (58.750000) | Client-side health: 58.0 (58.000000)

Reply
#2

The reason for this is that both GTA and the SA-MP server deal with the health as a float (4 bytes), but it's actually synced from the player to the server 1 byte in order to save bandwidth. So what you are seeing is basically a rounding problem.

This really should be fixed at some point.

What I can say is that the player's health and armour does match what you have there as the server-sided version, you just can't see it accurately with GetPlayerHealth/GetPlayerArmour.

SetPlayerHealth and SetPlayerArmour do send full floats, so the player will get those changes without any rounding problems.
Reply
#3

Quote:
Originally Posted by Kalcor
Посмотреть сообщение
The reason for this is that both GTA and the SA-MP server deal with the health as a float (4 bytes), but it's actually synced from the player to the server 1 byte in order to save bandwidth. So what you are seeing is basically a rounding problem.

This really should be fixed at some point.

What I can say is that the player's health and armour does match what you have there as the server-sided version, you just can't see it accurately with GetPlayerHealth/GetPlayerArmour.

SetPlayerHealth and SetPlayerArmour do send full floats, so the player will get those changes without any rounding problems.
I see. I was under the impression that OnPlayerTakeDamage was inaccurate but now I understand that it's actually GetPlayerHealth that isn't as reliable. I hope it will be fixed in the near future. Thank you for clarifying that.
Reply
#4

Quote:
Originally Posted by Kalcor
Посмотреть сообщение
The reason for this is that both GTA and the SA-MP server deal with the health as a float (4 bytes), but it's actually synced from the player to the server 1 byte in order to save bandwidth. So what you are seeing is basically a rounding problem.

This really should be fixed at some point.

What I can say is that the player's health and armour does match what you have there as the server-sided version, you just can't see it accurately with GetPlayerHealth/GetPlayerArmour.

SetPlayerHealth and SetPlayerArmour do send full floats, so the player will get those changes without any rounding problems.
I have a scripted health system and I do round down values of HP unless weapons used are not those where the damage is below 1. Just in case, but it's great to know this.
Reply
#5

Quote:
Originally Posted by CuervO
Посмотреть сообщение
I have a scripted health system and I do round down values of HP unless weapons used are not those where the damage is below 1. Just in case, but it's great to know this.
Exactly, just round up the health and it'll match.

pawn Код:
new iHealth = 1.0, iHealthEx, Float: fHealth = 1.4;

iHealthEx = floatround(fHealth, floatround_round);

if(iHealth != iHealthEx) { return false; } // It doesn't match
else if(iHealth == iHealthEx) { return true; } // It does match
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)