Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Client/game_sa/CVisibilityPluginsSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@

int CVisibilityPluginsSA::GetClumpAlpha(RpClump* pClump)
{
if (!pClump)
return 255;

using GetClumpAlpha = int(__cdecl*)(RpClump*);
return reinterpret_cast<GetClumpAlpha>(0x732B20)(pClump);
}

void CVisibilityPluginsSA::SetClumpAlpha(RpClump* pClump, int iAlpha)
{
if (!pClump)
return;

DWORD dwFunc = FUNC_CVisiblityPlugins_SetClumpAlpha;
// clang-format off
__asm
Expand Down
8 changes: 7 additions & 1 deletion Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4707,7 +4707,13 @@ bool CClientGame::VehicleCollisionHandler(CVehicleSAInterface*& pCollidingVehicl

pVehicleClientEntity->CallEvent("onClientVehicleCollision", Arguments, true);

// Update the colliding vehicle, because it might have been invalidated in onClientVehicleCollision (e.g. fixVehicle)
// Update the colliding vehicle, because it might have been invalidated in onClientVehicleCollision (e.g. fixVehicle, destroyElement)
if (pVehicleClientEntity->IsBeingDeleted() || !pVehicleClientEntity->GetGameEntity())
{
pCollidingVehicle = nullptr;
return true;
}

pCollidingVehicle = reinterpret_cast<CVehicleSAInterface*>(pVehicleClientEntity->GetGameEntity()->GetInterface());

// Allocate a BitStream
Expand Down
4 changes: 2 additions & 2 deletions Client/multiplayer_sa/CMultiplayerSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,8 +1225,8 @@ void CMultiplayerSA::InitHooks()
// Stop CPlayerPed::ProcessControl from calling CVisibilityPlugins::SetClumpAlpha
MemSet((void*)0x5E8E84, 0x90, 5);

// Stop CVehicle::UpdateClumpAlpha from calling CVisibilityPlugins::SetClumpAlpha
MemSet((void*)0x6D29CB, 0x90, 5);
// Disable CVehicle::UpdateClumpAlpha completely (fades singleplayer traffic, crashes at 0x732B2A when m_pRwObject is nullptr)
MemPut<BYTE>(0x6D2980, 0xC3);

// Disable CVehicle::DoDriveByShootings
MemSet((void*)0x741FD0, 0x90, 3);
Expand Down
152 changes: 151 additions & 1 deletion Client/multiplayer_sa/CMultiplayerSA_CrashFixHacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,87 @@ static void __declspec(naked) HOOK_CrashFix_Misc20()
// clang-format on
}

////////////////////////////////////////////////////////////////////////
// CMatrix::UpdateRwMatrix
//
// Prevent crash 0x59AD76 when an unaligned or invalid RwMatrix* is passed
////////////////////////////////////////////////////////////////////////
#define HOOKPOS_CrashFix_CMatrix__UpdateRwMatrix 0x59AD70
#define HOOKSIZE_CrashFix_CMatrix__UpdateRwMatrix 6
static const DWORD RETURN_CrashFix_CMatrix__UpdateRwMatrix = 0x59AD76;
static void __declspec(naked) HOOK_CrashFix_CMatrix__UpdateRwMatrix()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
mov eax, [esp+4] // m (RwMatrix*)
test eax, eax
jz invalid_matrix

test al, 3 // RwMatrix must be at least 4-byte aligned (RenderWare matrices are 16-byte aligned)
jnz invalid_matrix

cmp eax, 10000h // Guard against low/unmapped page addresses
jb invalid_matrix

// Valid pointer: restore overwritten instructions and continue normal path
mov edx, [ecx] // this->mat.right.x
jmp RETURN_CrashFix_CMatrix__UpdateRwMatrix

invalid_matrix:
push 20
call CrashAverted
xor eax, eax
retn 4
}
// clang-format on
}

////////////////////////////////////////////////////////////////////////
// CPlaceable::AllocateMatrix
//
// Zero out m_pAttachMatrix and m_bOwnsAttachedMatrix on newly allocated CMatrixLink
////////////////////////////////////////////////////////////////////////
#define HOOKPOS_CrashFix_AllocateMatrix_Init1 0x54F5A6
#define HOOKSIZE_CrashFix_AllocateMatrix_Init1 8
static void __declspec(naked) HOOK_CrashFix_AllocateMatrix_Init1()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
mov [eax+48h], esi // m_pOwner = this
mov [esi+14h], eax // this->m_pMatrix = eax
mov dword ptr [eax+40h], 0 // m_pAttachMatrix = nullptr
mov byte ptr [eax+44h], 0 // m_bOwnsAttachedMatrix = false
pop esi
retn
}
// clang-format on
}

#define HOOKPOS_CrashFix_AllocateMatrix_Init2 0x54F5C5
#define HOOKSIZE_CrashFix_AllocateMatrix_Init2 8
static void __declspec(naked) HOOK_CrashFix_AllocateMatrix_Init2()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
mov [eax+48h], esi // m_pOwner = this
mov [esi+14h], eax // this->m_pMatrix = eax
mov dword ptr [eax+40h], 0 // m_pAttachMatrix = nullptr
mov byte ptr [eax+44h], 0 // m_bOwnsAttachedMatrix = false
pop esi
retn
}
// clang-format on
}

////////////////////////////////////////////////////////////////////////
// CTaskSimpleCarFallOut::FinishAnimFallOutCB
//
Expand Down Expand Up @@ -4092,6 +4173,71 @@ static int _cdecl CFileLoader_LoadVehicleObject_sscanf(const char* s, const char
rearWheelSize, wheelUpgradeClass);
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// Crash at 0x732B2A in CVisibilityPlugins::GetClumpAlpha
//
// Root cause: In GTA:SA, CVisibilityPlugins::GetClumpAlpha (0x732B20) and SetClumpAlpha
// (0x732B00) read/write directly at [ecx+eax+4] where ecx is ms_clumpPluginOffset (0x34)
// and eax is RpClump*. When called on an entity with null m_pRwObject (e.g. during vehicle,
// ped, or object creation, destruction, streaming, or rendering), eax is nullptr, causing
// an access violation at 0x00000038.
//
// Fix: Hook both functions to check for null RpClump*. If null, GetClumpAlpha safely returns
// 0xFF (255, fully opaque) and SetClumpAlpha returns immediately without memory access.
//
//////////////////////////////////////////////////////////////////////////////////////////
#define HOOKPOS_CVisibilityPlugins_GetClumpAlpha 0x732B20
#define HOOKSIZE_CVisibilityPlugins_GetClumpAlpha 5
#define HOOKCHECK_CVisibilityPlugins_GetClumpAlpha 0x8B

static void __declspec(naked) HOOK_CVisibilityPlugins_GetClumpAlpha()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
mov eax, [esp+4]
test eax, eax
jz null_clump

mov ecx, ds:[0x8D6094]
mov eax, [ecx+eax+4]
retn

null_clump:
mov eax, 0xFF
retn
}
// clang-format on
}

#define HOOKPOS_CVisibilityPlugins_SetClumpAlpha 0x732B00
#define HOOKSIZE_CVisibilityPlugins_SetClumpAlpha 5
#define HOOKCHECK_CVisibilityPlugins_SetClumpAlpha 0x8B

static void __declspec(naked) HOOK_CVisibilityPlugins_SetClumpAlpha()
{
MTA_VERIFY_HOOK_LOCAL_SIZE;

// clang-format off
__asm
{
mov ecx, [esp+4]
test ecx, ecx
jz null_clump

mov eax, [esp+8]
mov edx, ds:[0x8D6094]
mov [edx+ecx+4], eax

null_clump:
retn
}
// clang-format on
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// Setup hooks for CrashFixHacks
Expand All @@ -4116,8 +4262,10 @@ void CMultiplayerSA::InitHooks_CrashFixHacks()
EZHookInstall(CrashFix_Misc16);
// EZHookInstall ( CrashFix_Misc17 );
EZHookInstall(CrashFix_Misc18);
// EZHookInstall ( CrashFix_Misc19 );
EZHookInstall(CrashFix_Misc20);
EZHookInstall(CrashFix_CMatrix__UpdateRwMatrix);
EZHookInstall(CrashFix_AllocateMatrix_Init1);
EZHookInstall(CrashFix_AllocateMatrix_Init2);
EZHookInstall(CrashFix_Misc21);
EZHookInstall(CrashFix_Misc22);
EZHookInstall(CrashFix_Misc23);
Expand Down Expand Up @@ -4180,6 +4328,8 @@ void CMultiplayerSA::InitHooks_CrashFixHacks()
EZHookInstallChecked(CStreaming__GetNextFileOnCd_NullTxdDef);
EZHookInstallChecked(CStreaming__ConvertBufferToObject_NullTxdDef);
EZHookInstallChecked(CEventScanner__ScanForEvents_ContactEntity);
EZHookInstallChecked(CVisibilityPlugins_GetClumpAlpha);
EZHookInstallChecked(CVisibilityPlugins_SetClumpAlpha);

// Install train crossing crashfix (the temporary variable is required for the template logic)
void (*temp)() = HOOK_TrainCrossingBarrierCrashFix<RETURN_CObject_Destructor_TrainCrossing_Check, RETURN_CObject_Destructor_TrainCrossing_Invalid>;
Expand Down
Loading
Loading