Skip to content

Fix health and armor puresync validation#5039

Open
QueryOfficial wants to merge 3 commits into
multitheftauto:masterfrom
QueryOfficial:fix/health-armor-sync
Open

Fix health and armor puresync validation#5039
QueryOfficial wants to merge 3 commits into
multitheftauto:masterfrom
QueryOfficial:fix/health-armor-sync

Conversation

@QueryOfficial

Copy link
Copy Markdown
Contributor

Summary

Align player health/armor puresync with gameplay limits and make server state authoritative.

  • Change SPlayerHealthSync max from 255 to 200 and SPlayerArmorSync max from 127.5 to 100
  • Clamp tracked health/armor before client puresync writes (CNetAPI, CPedSync) instead of reading raw GTA memory
  • Clamp server CPed::SetHealth() to GetMaxHealth()
  • Reject unauthorized health/armor increases received via puresync unless server previously authorized them (AuthorizeHealthChange / AuthorizeArmorChange)
  • Send correction RPCs (SET_ELEMENT_HEALTH / SET_PED_ARMOR) back to the syncing client when values are clamped or rejected
  • Add/update sync round-trip tests in SyncDataTypes_Tests.cpp

Complements #3983, which already added client-side setPedArmor validation and server armor clamping.

Motivation

Fixes #3791.

#3791 reported that client and server health/armor could diverge. For example, a client could use setPedArmor(me, 9999) while the server reflected wire-encoded caps (127.5 armor, up to 255 health) that did not match script limits or anti-cheat expectations.

This was discussed in #3791 with different views:

  • iiroak (reporter): the real problem is desync; server-side anti-cheat must be able to trust getPedArmor / getElementHealth
  • PlatinMTA: the fix belongs in puresync; client cap is needed; 127.5 / 255 come from 8-bit sync encoding, not gameplay limits
  • FileEX: server should safely clamp values on sync receive; optionally remove client setters (not done here)
  • TracerDS: max health should not be exceeded
  • ghost: armor 150 / HUD fixes should be separate work
  • Dutchman101: merged the urgent armor clamp fix in Fix armor limit and synchronization between client and server #3983

Related issues/commits reviewed while implementing this:

This PR intentionally does not include:

  • Raising armor cap to 150
  • Removing client-side setPedArmor / setElementHealth
  • Vehicle-style uncapped health/armor
  • HUD / setPedStat changes

Client and server must be updated together because sync struct ranges changed.

Test plan

Manual:

  1. crun setPedArmor(localPlayer, 9999) → client ≤ 100; srun getPedArmor(player) must never return 127.5
  2. crun setElementHealth(localPlayer, 999) → server getElementHealth(player)GetMaxHealth(), never 255
  3. Server setPedArmor(player, 75) → both sides report ~75
  4. createPickup armor/health pickup still works via server Use()
  5. Damage still triggers onPlayerDamage / onPedDamage with correct deltas
  6. Respawn restores authorized max health

Automated:

  • Run SyncDataTypes_Tests (updated ranges + new clamp round-trip tests)

Checklist

  • Your code should follow the coding guidelines.
  • Smaller pull requests are easier to review. If your pull request is beefy, your pull request should be reviewable commit-by-commit.

@Dryxio

Dryxio commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Was this PR carefully tested for edge cases ? I can already see that the auth token can stay valid for 3 seconds and may be reused after taking damage, allowing the player to heal back

if (fIncomingHealth > fOldHealth + FLOAT_EPSILON)
{
if (!TryConsumeHealthArmorAuth(m_healthAuth, fIncomingHealth, HEALTH_AUTH_EPSILON))
return fOldHealth;

…lass to enhance synchronization and prevent unauthorized changes
@QueryOfficial

Copy link
Copy Markdown
Contributor Author

Good catch you're right, I missed this edge case in the initial testing.

The token was only consumed on a matching increase, so taking damage in between left it valid and allowed syncing back to the authorized value within the 3s window.

Fixed in the latest commit: pending health/armor authorizations are now invalidated on a meaningful sync decrease (HEALTH_AUTH_EPSILON / ARMOR_AUTH_EPSILON), so heal-back after damage is rejected. The threshold matches the auth epsilon to avoid false invalidation from wire quantization (~0.78 health step).

Thanks for flagging this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

setPedArmor and setElementHealth synchronization problems from Client to Server

2 participants