@@ -1360,11 +1360,8 @@ void CVehicleSA::SetHandlingData(CHandlingEntry* pHandling)
13601360 // Store the handling and recalculate it
13611361 m_pHandlingData = static_cast <CHandlingEntrySA*>(pHandling);
13621362 pVehicleInterface->pHandlingData = m_pHandlingData->GetInterface ();
1363-
1364- // Only recalculate if collision model is loaded (needed for suspension lines)
1365- CModelInfo* pModelInfo = pGame->GetModelInfo (GetModelIndex ());
1366- if (pModelInfo && pModelInfo->GetInterface ()->pColModel && pModelInfo->GetInterface ()->pColModel ->m_data )
1367- RecalculateHandling ();
1363+
1364+ RecalculateHandling ();
13681365}
13691366
13701367void CVehicleSA::SetFlyingHandlingData (CFlyingHandlingEntry* pFlyingHandling)
@@ -1385,16 +1382,18 @@ void CVehicleSA::RecalculateHandling()
13851382 if (!pInt)
13861383 return ;
13871384
1388- // Ensure collision model is loaded before recalculating (needed for suspension lines)
13891385 CModelInfo* pModelInfo = pGame->GetModelInfo (GetModelIndex ());
1390- if (!pModelInfo || !pModelInfo->GetInterface ()->pColModel || !pModelInfo->GetInterface ()->pColModel ->m_data )
1391- return ;
1386+ auto * pModelInterface = pModelInfo ? pModelInfo->GetInterface () : nullptr ;
1387+ auto * pColModelInterface = pModelInterface ? pModelInterface->pColModel : nullptr ;
1388+ bool bSuspensionDataReady = pColModelInterface && pColModelInterface->m_data ;
1389+ bool bHasSuspension = pModelInfo &&
1390+ (pModelInfo->IsCar () || pModelInfo->IsMonsterTruck () || pModelInfo->IsTrailer () || pModelInfo->IsBike ());
13921391
13931392 m_pHandlingData->Recalculate ();
13941393
13951394 // Recalculate the suspension lines (only for vehicles that have suspension)
1396- // Already validated that pColModel and m_data exist above
1397- if (pModelInfo-> IsCar () || pModelInfo-> IsMonsterTruck () || pModelInfo-> IsTrailer () || pModelInfo-> IsBike () )
1395+ // Skip until collision data streams in to avoid accessing null suspension lines
1396+ if (bSuspensionDataReady && bHasSuspension )
13981397 RecalculateSuspensionLines ();
13991398
14001399 // Put it in our interface
@@ -1846,13 +1845,30 @@ void CVehicleSA::RecalculateSuspensionLines()
18461845 return ;
18471846
18481847 // Ensure collision model is loaded before setting up suspension
1849- if (!pModelInfo->GetInterface ()->pColModel || !pModelInfo->GetInterface ()->pColModel ->m_data )
1848+ auto * pColModelInterface = pModelInfo->GetInterface ()->pColModel ;
1849+ if (!pColModelInterface || !pColModelInterface->m_data )
18501850 return ;
18511851
1852- // Note: We skip calling SetupSuspensionLines() because it's GTA SA's native code that can
1853- // access pColModel->m_data without validation. If collision model is unloaded during execution
1854- // (race condition), it causes crashes. CopyGlobalSuspensionLinesToPrivate() is safer as it
1855- // validates collision model before accessing.
1852+ struct ScopedModelRef
1853+ {
1854+ explicit ScopedModelRef (CModelInfo* model)
1855+ : m_Model(model)
1856+ {
1857+ if (m_Model)
1858+ m_Model->ModelAddRef (EModelRequestType::BLOCKING, " CVehicleSA::RecalculateSuspensionLines" );
1859+ }
1860+
1861+ ~ScopedModelRef ()
1862+ {
1863+ if (m_Model)
1864+ m_Model->RemoveRef ();
1865+ }
1866+
1867+ CModelInfo* m_Model;
1868+ } modelRef (pModelInfo);
1869+
1870+ GetVehicleInterface ()->SetupSuspensionLines ();
1871+
18561872 CopyGlobalSuspensionLinesToPrivate ();
18571873 }
18581874}
0 commit comments