@@ -1360,8 +1360,11 @@ 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- RecalculateHandling ();
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 ();
13651368}
13661369
13671370void CVehicleSA::SetFlyingHandlingData (CFlyingHandlingEntry* pFlyingHandling)
@@ -1382,18 +1385,16 @@ void CVehicleSA::RecalculateHandling()
13821385 if (!pInt)
13831386 return ;
13841387
1388+ // Ensure collision model is loaded before recalculating (needed for suspension lines)
13851389 CModelInfo* pModelInfo = pGame->GetModelInfo (GetModelIndex ());
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 ());
1390+ if (!pModelInfo || !pModelInfo->GetInterface ()->pColModel || !pModelInfo->GetInterface ()->pColModel ->m_data )
1391+ return ;
13911392
13921393 m_pHandlingData->Recalculate ();
13931394
13941395 // Recalculate the suspension lines (only for vehicles that have suspension)
1395- // Skip until collision data streams in to avoid accessing null suspension lines
1396- if (bSuspensionDataReady && bHasSuspension )
1396+ // Already validated that pColModel and m_data exist above
1397+ if (pModelInfo-> IsCar () || pModelInfo-> IsMonsterTruck () || pModelInfo-> IsTrailer () || pModelInfo-> IsBike () )
13971398 RecalculateSuspensionLines ();
13981399
13991400 // Put it in our interface
@@ -1838,37 +1839,20 @@ void CVehicleSA::RecalculateSuspensionLines()
18381839
18391840 DWORD dwModel = GetModelIndex ();
18401841 CModelInfo* pModelInfo = pGame->GetModelInfo (dwModel);
1841- if (pModelInfo && ( pModelInfo->IsCar () || pModelInfo-> IsMonsterTruck () || pModelInfo->IsTrailer () ))
1842+ if (pModelInfo && pModelInfo->IsMonsterTruck () || pModelInfo->IsCar ( ))
18421843 {
18431844 // Trains (Their trailers do as well!)
18441845 if (pModelInfo->IsTrain () || dwModel == 571 || dwModel == 570 || dwModel == 569 || dwModel == 590 )
18451846 return ;
18461847
18471848 // Ensure collision model is loaded before setting up suspension
1848- auto * pColModelInterface = pModelInfo->GetInterface ()->pColModel ;
1849- if (!pColModelInterface || !pColModelInterface->m_data )
1849+ if (!pModelInfo->GetInterface ()->pColModel || !pModelInfo->GetInterface ()->pColModel ->m_data )
18501850 return ;
18511851
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-
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.
18721856 CopyGlobalSuspensionLinesToPrivate ();
18731857 }
18741858}
0 commit comments