Skip to content

Commit

Permalink
Fix invalid target and owner references in projectiles (#3879)
Browse files Browse the repository at this point in the history
Fixes crash #3876
  • Loading branch information
TheNormalnij authored Dec 14, 2024
1 parent 81218c7 commit 9ab6104
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Client/game_sa/CPlayerPedSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "CPlayerInfoSA.h"
#include "CPlayerPedSA.h"
#include "CWorldSA.h"
#include "CProjectileInfoSA.h"

extern CCoreInterface* g_pCore;
extern CGameSA* pGame;
Expand Down Expand Up @@ -137,6 +138,7 @@ CPlayerPedSA::~CPlayerPedSA()
if ((DWORD)GetInterface()->vtbl != VTBL_CPlaceable)
{
CWorldSA* world = (CWorldSA*)pGame->GetWorld();
pGame->GetProjectileInfo()->RemoveEntityReferences(this);
world->Remove(m_pInterface, CPlayerPed_Destructor);

DWORD dwThis = (DWORD)m_pInterface;
Expand Down
15 changes: 15 additions & 0 deletions Client/game_sa/CProjectileInfoSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,18 @@ DWORD CProjectileInfoSA::GetCounter()
{
return internalInterface->dwCounter - pGame->GetSystemTime();
}

void CProjectileInfoSA::RemoveEntityReferences(CEntity* entity)
{
const CEntitySAInterface* entityInterface = entity->GetInterface();
for (int i = 0; i < PROJECTILE_INFO_COUNT; i++)
{
auto projectileInterface = projectileInfo[i]->internalInterface;

if (projectileInterface->pEntProjectileOwner == entityInterface)
projectileInterface->pEntProjectileOwner = nullptr;

if (projectileInterface->pEntProjectileTarget == entityInterface)
projectileInterface->pEntProjectileTarget = nullptr;
}
}
4 changes: 3 additions & 1 deletion Client/game_sa/CProjectileInfoSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class CProjectileInfoSAInterface
};
// #pragma pack(pop)

class CProjectileInfoSA : public CProjectileInfo
// TODO extract manager class
class CProjectileInfoSA final : public CProjectileInfo
{
private:
CProjectileInfoSA* projectileInfo[PROJECTILE_INFO_COUNT];
Expand All @@ -65,6 +66,7 @@ class CProjectileInfoSA : public CProjectileInfo
CProjectileInfo* GetProjectileInfo(DWORD dwIndex);
bool AddProjectile(CEntity* creator, eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity);
CProjectile* GetProjectile(void* projectilePointer);
void RemoveEntityReferences(CEntity* entity);

CEntity* GetTarget();
void SetTarget(CEntity* pEntity);
Expand Down
1 change: 1 addition & 0 deletions Client/game_sa/CVehicleSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ CVehicleSA::~CVehicleSA()
}

CWorldSA* pWorld = (CWorldSA*)pGame->GetWorld();
pGame->GetProjectileInfo()->RemoveEntityReferences(this);
pWorld->Remove(m_pInterface, CVehicle_Destructor);
pWorld->RemoveReferencesToDeletedObject(m_pInterface);

Expand Down
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <game/Task.h>
#include <game/CBuildingRemoval.h>
#include "game/CClock.h"
#include <game/CProjectileInfo.h>
#include <windowsx.h>
#include "CServerInfo.h"

Expand Down Expand Up @@ -403,6 +404,10 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())
CClientGame::~CClientGame()
{
m_bBeingDeleted = true;
// Remove active projectile references to local player
if (auto pLocalPlayer = g_pClientGame->GetLocalPlayer())
g_pGame->GetProjectileInfo()->RemoveEntityReferences(pLocalPlayer->GetGameEntity());

// Stop all explosions. Unfortunately this doesn't fix the crash
// if a vehicle is destroyed while it explodes.
g_pGame->GetExplosionManager()->RemoveAllExplosions();
Expand Down
1 change: 1 addition & 0 deletions Client/sdk/game/CProjectileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CProjectileInfo
virtual CProjectileInfo* GetProjectileInfo(void* projectileInfoInterface) = 0; // don't use
virtual void RemoveProjectile(CProjectileInfo* pProjectileInfo, CProjectile* pProjectile, bool bBlow = true) = 0;
virtual CProjectileInfo* GetProjectileInfo(DWORD Index) = 0;
virtual void RemoveEntityReferences(CEntity* entity) = 0;

virtual CEntity* GetTarget() = 0;
virtual void SetTarget(CEntity* pEntity) = 0;
Expand Down

0 comments on commit 9ab6104

Please sign in to comment.