Skip to content

Commit 9842a0e

Browse files
Synchronize changes from 1.6 master branch [ci skip]
0f01e5d Death Event Parameters Synchronization Fix (#4418)
2 parents b71d7d2 + 0f01e5d commit 9842a0e

File tree

7 files changed

+117
-10
lines changed

7 files changed

+117
-10
lines changed

Client/mods/deathmatch/logic/CClientGame.cpp

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <game/CTaskManager.h>
3030
#include <game/CWanted.h>
3131
#include <game/CWeapon.h>
32+
#include <game/CWeaponInfo.h>
3233
#include <game/CWeaponStatManager.h>
3334
#include <game/CWeather.h>
3435
#include <game/Task.h>
@@ -38,6 +39,7 @@
3839
#include <game/CVehicleAudioSettingsManager.h>
3940
#include <windowsx.h>
4041
#include "CServerInfo.h"
42+
#include "CClientPed.h"
4143

4244
SString StringZeroPadout(const SString& strInput, uint uiPadoutSize)
4345
{
@@ -102,10 +104,11 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())
102104
m_TargetedPlayerID = INVALID_ELEMENT_ID;
103105
m_pDamageEntity = NULL;
104106
m_DamagerID = INVALID_ELEMENT_ID;
105-
m_ucDamageBodyPiece = 0xFF;
106-
m_ucDamageWeapon = 0xFF;
107+
m_ucDamageBodyPiece = BODYPART_INVALID;
108+
m_ucDamageWeapon = WEAPONTYPE_INVALID;
107109
m_ulDamageTime = 0;
108110
m_bDamageSent = true;
111+
m_serverProcessedDeath = false;
109112
m_bShowNetstat = false;
110113
m_bShowFPS = false;
111114
m_bHudAreaNameDisabled = false;
@@ -1456,10 +1459,18 @@ void CClientGame::DoPulses()
14561459
if (CClientTime::GetTime() - m_ulDamageTime > 2000)
14571460
{
14581461
m_DamagerID = INVALID_ELEMENT_ID;
1459-
m_ucDamageWeapon = 0xFF;
1460-
m_ucDamageBodyPiece = 0xFF;
1462+
m_ucDamageWeapon = WEAPONTYPE_INVALID;
1463+
m_ucDamageBodyPiece = BODYPART_INVALID;
1464+
}
1465+
// Check if we need to trigger death event
1466+
if (!m_pLocalPlayer->IsDeadOnNetwork() && m_pLocalPlayer->GetHealth() == 0.0f)
1467+
{
1468+
// Only call DoWastedCheck if server hasn't already processed this death
1469+
// This prevents duplicate events when server processes death via unified context system
1470+
if (!m_serverProcessedDeath) {
1471+
DoWastedCheck(m_DamagerID, m_ucDamageWeapon, m_ucDamageBodyPiece);
1472+
}
14611473
}
1462-
DoWastedCheck(m_DamagerID, m_ucDamageWeapon, m_ucDamageBodyPiece);
14631474
}
14641475

14651476
// Game hacks, restore certain variables
@@ -2002,7 +2013,7 @@ void CClientGame::UpdateFireKey()
20022013
{
20032014
if (pTargetPed->IsLocalEntity())
20042015
{
2005-
CStaticFunctionDefinitions::KillPed(*pTargetPed, m_pLocalPlayer, 4 /*WEAPONTYPE_KNIFE*/, 9 /*BODYPART_HEAD*/, true);
2016+
CStaticFunctionDefinitions::KillPed(*pTargetPed, m_pLocalPlayer, WEAPONTYPE_KNIFE, BODYPART_HEAD, true);
20062017
return;
20072018
}
20082019

@@ -4502,7 +4513,9 @@ bool CClientGame::ApplyPedDamageFromGame(eWeaponType weaponUsed, float fDamage,
45024513
GetDeathAnim(pDamagedPed, pEvent, animGroup, animID);
45034514

45044515
// Check if we're dead
4505-
DoWastedCheck(damagerID, weaponUsed, hitZone, animGroup, animID);
4516+
if (!m_serverProcessedDeath) {
4517+
DoWastedCheck(damagerID, weaponUsed, hitZone, animGroup, animID);
4518+
}
45064519
}
45074520

45084521
// Allow GTA to kill us if we've fell to our death
@@ -4570,6 +4583,14 @@ void CClientGame::DeathHandler(CPed* pKilledPedSA, unsigned char ucDeathReason,
45704583
if (!pKilledPed)
45714584
return;
45724585

4586+
// For local player in vehicle explosions, set damage data for consistent client events
4587+
if (IS_PLAYER(pKilledPed) && pKilledPed->IsLocalPlayer() && ucDeathReason == WEAPONTYPE_EXPLOSION)
4588+
{
4589+
// Set explosion damage data so DoWastedCheck uses correct parameters
4590+
SetExplosionDamageData();
4591+
return; // Local player death is handled by DoWastedCheck
4592+
}
4593+
45734594
// Not required for remote players. Local player is handled in DoPulses->DoWastedCheck
45744595
if (IS_PLAYER(pKilledPed))
45754596
return;
@@ -5596,11 +5617,11 @@ void CClientGame::DoWastedCheck(ElementID damagerID, unsigned char ucWeapon, uns
55965617
Arguments.PushElement(pKiller);
55975618
else
55985619
Arguments.PushBoolean(false);
5599-
if (ucWeapon != 0xFF)
5620+
if (ucWeapon != WEAPONTYPE_INVALID)
56005621
Arguments.PushNumber(ucWeapon);
56015622
else
56025623
Arguments.PushBoolean(false);
5603-
if (ucBodyPiece != 0xFF)
5624+
if (ucBodyPiece != BODYPART_INVALID)
56045625
Arguments.PushNumber(ucBodyPiece);
56055626
else
56065627
Arguments.PushBoolean(false);
@@ -7150,3 +7171,20 @@ void CClientGame::AudioZoneRadioSwitchHandler(DWORD dwStationID)
71507171
g_pGame->GetAudioEngine()->StartRadio(dwStationID);
71517172
}
71527173
}
7174+
7175+
//////////////////////////////////////////////////////////////////
7176+
//
7177+
// CClientGame::TryGetCurrentWeapon
7178+
//
7179+
// Helper method to get current weapon type with error handling
7180+
// Returns actual weapon type or WEAPONTYPE_UNARMED as fallback
7181+
//
7182+
//////////////////////////////////////////////////////////////////
7183+
std::uint8_t CClientGame::TryGetCurrentWeapon(CClientPlayer* player)
7184+
{
7185+
if (!player)
7186+
return WEAPONTYPE_UNARMED;
7187+
7188+
eWeaponType weaponType = player->GetCurrentWeaponType();
7189+
return (weaponType != WEAPONTYPE_INVALID) ? static_cast<std::uint8_t>(weaponType) : WEAPONTYPE_UNARMED;
7190+
}

Client/mods/deathmatch/logic/CClientGame.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,44 @@ class CClientGame
405405
AnimationId animId = 15);
406406
void SendPedWastedPacket(CClientPed* Ped, ElementID damagerID = INVALID_ELEMENT_ID, unsigned char ucWeapon = 0xFF, unsigned char ucBodyPiece = 0xFF,
407407
AssocGroupId animGroup = 0, AnimationId animID = 15);
408+
409+
void ClearDamageData() noexcept {
410+
m_DamagerID = INVALID_ELEMENT_ID;
411+
m_ucDamageWeapon = WEAPONTYPE_INVALID;
412+
m_ucDamageBodyPiece = BODYPART_INVALID;
413+
m_ulDamageTime = 0;
414+
m_serverProcessedDeath = true;
415+
}
416+
417+
void ResetDeathProcessingFlag() noexcept {
418+
m_serverProcessedDeath = false;
419+
}
420+
421+
void SetScriptedDeathData() {
422+
auto* localPlayer = GetLocalPlayer();
423+
if (!localPlayer) {
424+
m_DamagerID = INVALID_ELEMENT_ID;
425+
m_ucDamageWeapon = WEAPONTYPE_INVALID;
426+
m_ucDamageBodyPiece = BODYPART_INVALID;
427+
m_ulDamageTime = CClientTime::GetTime();
428+
m_serverProcessedDeath = false;
429+
return;
430+
}
431+
432+
m_DamagerID = INVALID_ELEMENT_ID;
433+
m_ucDamageWeapon = TryGetCurrentWeapon(localPlayer);
434+
m_ucDamageBodyPiece = BODYPART_TORSO;
435+
m_ulDamageTime = CClientTime::GetTime();
436+
m_serverProcessedDeath = false;
437+
}
438+
439+
void SetExplosionDamageData() noexcept {
440+
m_DamagerID = INVALID_ELEMENT_ID;
441+
m_ucDamageWeapon = WEAPONTYPE_EXPLOSION;
442+
m_ucDamageBodyPiece = BODYPART_TORSO;
443+
m_ulDamageTime = CClientTime::GetTime();
444+
m_serverProcessedDeath = false;
445+
}
408446

409447
CClientGUIElement* GetClickedGUIElement() { return m_pClickedGUIElement; }
410448
void SetClickedGUIElement(CClientGUIElement* pElement) { m_pClickedGUIElement = NULL; }
@@ -616,6 +654,9 @@ class CClientGame
616654
AnimationId DrivebyAnimationHandler(AnimationId animGroup, AssocGroupId animId);
617655
void AudioZoneRadioSwitchHandler(DWORD dwStationID);
618656

657+
// Helper method to get current weapon type with error handling
658+
std::uint8_t TryGetCurrentWeapon(CClientPlayer* player);
659+
619660
static bool StaticProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
620661
bool ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
621662

@@ -770,6 +811,7 @@ class CClientGame
770811
unsigned char m_ucDamageBodyPiece;
771812
unsigned long m_ulDamageTime;
772813
bool m_bDamageSent;
814+
bool m_serverProcessedDeath{false}; // Flag to track server-processed deaths
773815

774816
eWeaponSlot m_lastWeaponSlot;
775817
SFixedArray<DWORD, WEAPONSLOT_MAX + 1> m_wasWeaponAmmoInClip;

Client/mods/deathmatch/logic/CClientPed.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum eBodyPart
6161
BODYPART_LEFT_LEG = 7,
6262
BODYPART_RIGHT_LEG = 8,
6363
BODYPART_HEAD = 9,
64+
BODYPART_INVALID = 255,
6465
};
6566

6667
enum eMovementState

Client/mods/deathmatch/logic/CPacketHandler.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,11 @@ void CPacketHandler::Packet_PlayerSpawn(NetBitStreamInterface& bitStream)
11371137

11381138
// He's no longer dead
11391139
pPlayer->SetDeadOnNetwork(false);
1140+
1141+
// Reset death processing flag for new life
1142+
if (pPlayer->IsLocalPlayer()) {
1143+
g_pClientGame->ResetDeathProcessingFlag();
1144+
}
11401145

11411146
// Reset weapons
11421147
pPlayer->RemoveAllWeapons();
@@ -1216,6 +1221,12 @@ void CPacketHandler::Packet_PlayerWasted(NetBitStreamInterface& bitStream)
12161221
}
12171222
// Update our sync-time context
12181223
pPed->SetSyncTimeContext(ucTimeContext);
1224+
1225+
// Clear stale damage data if this is the local player
1226+
// This prevents DoWastedCheck from firing with stale data when server processes death
1227+
if (pPed->IsLocalPlayer()) {
1228+
g_pClientGame->ClearDamageData();
1229+
}
12191230

12201231
// To at least here needs to be done on the local player to avoid desync
12211232
// Caz: Issue 8148 - Desync when calling spawnPlayer from an event handler remotely triggered from within onClientPlayerWasted

Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,12 @@ bool CStaticFunctionDefinitions::SetElementHealth(CClientEntity& Entity, float f
14991499
// Grab the model
15001500
CClientPed& Ped = static_cast<CClientPed&>(Entity);
15011501

1502+
// If setting health to 0 for local player, clear stale damage data
1503+
// and set proper scripted death parameters for DoWastedCheck
1504+
if (fHealth == 0.0f && Ped.IsLocalPlayer() && Ped.GetHealth() > 0.0f) {
1505+
g_pClientGame->SetScriptedDeathData();
1506+
}
1507+
15021508
// Set the new health
15031509
Ped.SetHealth(Clamp(0.0f, fHealth, Ped.GetMaxHealth()));
15041510
return true;

Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,14 @@ void CElementRPCs::SetElementHealth(CClientEntity* pSource, NetBitStreamInterfac
466466
if (pPed->IsHealthLocked())
467467
pPed->LockHealth(fHealth);
468468
else
469+
{
469470
pPed->SetHealth(fHealth);
471+
// If server sets health to 0 for local player, mark as server-processed death
472+
// to prevent DoWastedCheck from firing with stale local damage data
473+
if (fHealth == 0.0f && pPed->IsLocalPlayer()) {
474+
g_pClientGame->ClearDamageData();
475+
}
476+
}
470477
break;
471478
}
472479

Client/multiplayer_sa/multiplayer_shotsync.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1691,7 +1691,9 @@ void CEventVehicleExplosion_NotifyDeathmatch()
16911691
CPed* pPed = pPedClientEntity ? pPedClientEntity->pEntity : nullptr;
16921692

16931693
if (pPed)
1694-
m_pDeathHandler(pPed, 63, 3);
1694+
{
1695+
m_pDeathHandler(pPed, WEAPONTYPE_EXPLOSION, 3);
1696+
}
16951697
}
16961698
}
16971699

0 commit comments

Comments
 (0)