void CClientThinkList::PerformThinkFunctions() { float curtime = gpGlobals->curtime; unsigned long iNext; for ( unsigned long iCur=m_ThinkEntries.Head(); iCur != m_ThinkEntries.InvalidIndex(); iCur = iNext ) { iNext = m_ThinkEntries.Next( iCur ); CThinkEntry *pEntry = &m_ThinkEntries[iCur]; IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( pEntry->m_hEnt ); if ( pThink ) { if ( pEntry->m_flNextClientThink == CLIENT_THINK_ALWAYS ) { // NOTE: The Think function here could call SetNextClientThink // which would cause it to be removed + readded into the list pThink->ClientThink(); // NOTE: The Think() call can cause other things to be added to the Think // list, which could reallocate memory and invalidate the pEntry pointer pEntry = &m_ThinkEntries[iCur]; } else if ( pEntry->m_flNextClientThink < curtime ) { pEntry->m_flNextClientThink = curtime; // NOTE: The Think function here could call SetNextClientThink // which would cause it to be readded into the list pThink->ClientThink(); // NOTE: The Think() call can cause other things to be added to the Think // list, which could reallocate memory and invalidate the pEntry pointer pEntry = &m_ThinkEntries[iCur]; // If they haven't changed the think time, then it should be removed. if ( pEntry->m_flNextClientThink == curtime ) { RemoveThinkable( pEntry->m_hEnt ); } } Assert( pEntry == &m_ThinkEntries[iCur] ); // Set this after the Think calls in case they look at LastClientThink m_ThinkEntries[iCur].m_flLastClientThink = curtime; } else { // This should be almost impossible. When ClientEntityHandle_t's are versioned, // this should be totally impossible. Assert( false ); m_ThinkEntries.Remove( iCur ); } } // Clear out the client-side entity deletion list. CleanUpDeleteList(); }
//----------------------------------------------------------------------------- // Think for all entities that need it //----------------------------------------------------------------------------- void CClientThinkList::PerformThinkFunctions() { VPROF_("Client Thinks", 1, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT); int nMaxList = m_ThinkEntries.Count(); if ( nMaxList == 0 ) return; ++m_nIterEnum; // Build a list of entities to think this frame, in order of hierarchy. // Do this because the list may be modified during the thinking and also to // prevent bad situations where an entity can think more than once in a frame. ThinkEntry_t **ppThinkEntryList = (ThinkEntry_t**)stackalloc( nMaxList * sizeof(ThinkEntry_t*) ); int nThinkCount = 0; for ( unsigned short iCur=m_ThinkEntries.Head(); iCur != m_ThinkEntries.InvalidIndex(); iCur = m_ThinkEntries.Next( iCur ) ) { AddEntityToFrameThinkList( &m_ThinkEntries[iCur], false, nThinkCount, ppThinkEntryList ); Assert( nThinkCount <= nMaxList ); } // While we're in the loop, no changes to the think list are allowed m_bInThinkLoop = true; // Perform thinks on all entities that need it int i; for ( i = 0; i < nThinkCount; ++i ) { PerformThinkFunction( ppThinkEntryList[i], gpGlobals->curtime ); } m_bInThinkLoop = false; // Apply changes to the think list int nCount = m_aChangeList.Count(); for ( i = 0; i < nCount; ++i ) { ClientThinkHandle_t hThink = m_aChangeList[i].m_hThink; if ( hThink != INVALID_THINK_HANDLE ) { // This can happen if the same think handle was removed twice if ( !m_ThinkEntries.IsInList( (unsigned long)hThink ) ) continue; // NOTE: This is necessary for the case where the client entity handle // is slammed to NULL during a think interval; the hThink will get stuck // in the list and can never leave. SetNextClientThink( hThink, m_aChangeList[i].m_flNextTime ); } else { SetNextClientThink( m_aChangeList[i].m_hEnt, m_aChangeList[i].m_flNextTime ); } } m_aChangeList.RemoveAll(); // Clear out the client-side entity deletion list. CleanUpDeleteList(); }