citizenfx / fivem

The source code for the Cfx.re modification frameworks, such as FiveM, RedM and LibertyM, as well as FXServer.
https://cfx.re/
3.48k stars 2.06k forks source link

Server-side GetEntityHealth is off from usual health displaying. #2657

Open Keyinator opened 1 month ago

Keyinator commented 1 month ago

What happened?

I wrote a small script which upon request by the client, calculates the player's health on the server and sends it to the client. On the client it is then compared to the usual Natives / Values.

I deployed the below script and shot a player in the foot periodically. These are the results:

[    286734] [cl2_b2699_GTAP]             MainThrd/ Health: (194)94, Armor: (79)79
[    289796] [cl2_b2699_GTAP]             MainThrd/ Health: (189)89, Armor: (59)59
[    291156] [cl2_b2699_GTAP]             MainThrd/ Health: (184)84, Armor: (39)39
[    292453] [cl2_b2699_GTAP]             MainThrd/ Health: (179)79, Armor: (19)19
[    293515] [cl2_b2699_GTAP]             MainThrd/ Health: (173)73, Armor: (0)0
[    294562] [cl2_b2699_GTAP]             MainThrd/ Health: (148)48, Armor: (0)0
[    295625] [cl2_b2699_GTAP]             MainThrd/ Health: (123)23, Armor: (0)0

On the client health is between 0-100 while on the server it is between 100-200

Long time ago health was between 0-200 my guess is that both server and client have been changed to a range of 100 but somehow the server has kept a delta of +100.

Expected result

The same value should be shown for health (like it is with armor)

Reproduction steps

public class Test : ClientScript {
    [EventHandler("gameEventTriggered")]
    internal void GameEventTriggered(string eventName, object[] data) {
        if (!"CEventNetworkEntityDamage".Equals(eventName)) return;

        Entity victim = Entity.FromHandle(int.Parse(data[0].ToString()));
        if (victim != Game.PlayerPed) return;

        Entity attacker = Entity.FromHandle(int.Parse(data[1].ToString()));
        bool victimDied = int.Parse(data[5].ToString()) == 1;
        //uint weaponHash = (uint)int.Parse(data[6].ToString());
        //bool isMeleeDamage = int.Parse(data[9].ToString()) != 0;
        //int vehicleDamageTypeFlag = int.Parse(data[10].ToString());

        Events.TriggerServerEvent("ShowMyHealth_Request");
    }

    [EventHandler("ShowMyHealth_Answer")]
    internal void ShowMyHealth(int health, int armor) {
        Debug.WriteLine($"Health: ({health}){Game.PlayerPed.Health}, Armor: ({armor}){Game.PlayerPed.Armor}");
    }
}

public class Test : ServerScript {
    [EventHandler("ShowMyHealth_Request")]
    internal async Coroutine ShowMyHealth([Source] Player player) {
        await Coroutine.Delay(100); //Wait so health is properly synced first

        int ped = Natives.GetPlayerPed(player.Handle.ToString());

        int health = Natives.GetEntityHealth(ped);
        int armor = Natives.GetPedArmour(ped);

        player.TriggerEvent("ShowMyHealth_Answer", health, armor);
    }
}

Importancy

There's a workaround

Area(s)

FiveM, FXServer, Natives, ScRT: C#

Specific version(s)

Version: FXServer-master v1.0.0.9019 linux with OneSync enabled

Additional information

I am unsure whether this only affects C# or other ScRT

AvarianKnight commented 1 month ago

This isn't a bug, this is expected behavior for the C# runtime, using the raw natives will get you the raw health, using the wrappers will get you the actual usable health

/// <summary>
/// Gets or sets the health of this <see cref="Entity"/> as an <see cref="int"/>.
/// </summary>
/// <value>
/// The health from 0 - 100 as an integer.
/// </value>
/// <remarks>if you need to get or set the value strictly, use <see cref="HealthFloat"/> instead.</remarks>
public int Health
{
    get
    {
        return API.GetEntityHealth(Handle) - 100;
    }
    set
    {
        API.SetEntityHealth(Handle, value + 100);
    }
}
Keyinator commented 1 month ago

@AvarianKnight Thanks for the info!

But why would the native health range from 100-200? Is this the range the base game uses?

Edit: Also in https://docs.fivem.net/natives/?_0x8E3222B7 it is described as

[...] If the entity is a ped it will return 0-200 [...]

Wouldn't this be wrong since it is 100-200 then?

matthias18771 commented 1 month ago

mp_m_freemode_01 has 200 max health, mp_f_freemode_01 has 175 max health by default. So yeah, the game uses that as far as I know. Kinda weird way of handling it by just -100, wouldn't that cause 75 max health on female peds?

AvarianKnight commented 1 month ago

@AvarianKnight Thanks for the info!

But why would the native health range from 100-200? Is this the range the base game uses?

Edit: Also in https://docs.fivem.net/natives/?_0x8E3222B7 it is described as

[...] If the entity is a ped it will return 0-200 [...]

Wouldn't this be wrong since it is 100-200 then?

For peds it would only return 100-200, yes. I'm not sure if there's other cases where it can return below 100, but I'm assuming not

AvarianKnight commented 1 month ago

mp_m_freemode_01 has 200 max health, mp_f_freemode_01 has 175 max health by default. So yeah, the game uses that as far as I know. Kinda weird way of handling it by just -100, wouldn't that cause 75 max health on female peds?

Yes, female peds only have 75 max health.