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();
}