mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-09-20 04:26:03 +08:00
Added original SDK code for Alien Swarm.
This commit is contained in:
426
game/client/client_thinklist.cpp
Normal file
426
game/client/client_thinklist.cpp
Normal file
@ -0,0 +1,426 @@
|
||||
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tier0/vprof.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
CClientThinkList g_ClientThinkList;
|
||||
|
||||
|
||||
static ConVar report_clientthinklist( "report_clientthinklist", "0", FCVAR_CHEAT, "List all clientside entities thinking and time - will report and turn itself off." );
|
||||
|
||||
|
||||
CClientThinkList::CClientThinkList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CClientThinkList::~CClientThinkList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Methods of IGameSystem
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CClientThinkList::Init()
|
||||
{
|
||||
m_nIterEnum = 0;
|
||||
m_bInThinkLoop = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CClientThinkList::Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CClientThinkList::LevelInitPreEntity()
|
||||
{
|
||||
m_nIterEnum = 0;
|
||||
}
|
||||
|
||||
void CClientThinkList::LevelShutdownPreEntity()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CClientThinkList::LevelShutdownPostEntity()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CClientThinkList::PreRender()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CClientThinkList::Update( float frametime )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets the client think
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::SetNextClientThink( ClientThinkHandle_t hThink, float flNextTime )
|
||||
{
|
||||
if ( hThink == INVALID_THINK_HANDLE )
|
||||
return;
|
||||
|
||||
if ( m_bInThinkLoop )
|
||||
{
|
||||
// Queue up all changes
|
||||
int i = m_aChangeList.AddToTail();
|
||||
m_aChangeList[i].m_hEnt = INVALID_CLIENTENTITY_HANDLE;
|
||||
m_aChangeList[i].m_hThink = hThink;
|
||||
m_aChangeList[i].m_flNextTime = flNextTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( flNextTime == CLIENT_THINK_NEVER )
|
||||
{
|
||||
RemoveThinkable( hThink );
|
||||
}
|
||||
else
|
||||
{
|
||||
GetThinkEntry( hThink )->m_flNextClientThink = flNextTime;
|
||||
}
|
||||
}
|
||||
|
||||
void CClientThinkList::SetNextClientThink( ClientEntityHandle_t hEnt, float flNextTime )
|
||||
{
|
||||
if ( flNextTime == CLIENT_THINK_NEVER )
|
||||
{
|
||||
RemoveThinkable( hEnt );
|
||||
return;
|
||||
}
|
||||
|
||||
IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( hEnt );
|
||||
if ( !pThink )
|
||||
return;
|
||||
|
||||
ClientThinkHandle_t hThink = pThink->GetThinkHandle();
|
||||
|
||||
if ( m_bInThinkLoop )
|
||||
{
|
||||
// Queue up all changes
|
||||
int i = m_aChangeList.AddToTail();
|
||||
m_aChangeList[i].m_hEnt = hEnt;
|
||||
m_aChangeList[i].m_hThink = hThink;
|
||||
m_aChangeList[i].m_flNextTime = flNextTime;
|
||||
return;
|
||||
}
|
||||
|
||||
// Add it to the list if it's not already in there.
|
||||
if ( hThink == INVALID_THINK_HANDLE )
|
||||
{
|
||||
hThink = (ClientThinkHandle_t)m_ThinkEntries.AddToTail();
|
||||
pThink->SetThinkHandle( hThink );
|
||||
|
||||
ThinkEntry_t *pEntry = GetThinkEntry( hThink );
|
||||
pEntry->m_hEnt = hEnt;
|
||||
pEntry->m_nIterEnum = -1;
|
||||
pEntry->m_flLastClientThink = 0.0f;
|
||||
}
|
||||
|
||||
Assert( GetThinkEntry( hThink )->m_hEnt == hEnt );
|
||||
GetThinkEntry( hThink )->m_flNextClientThink = flNextTime;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Removes the thinkable from the list
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::RemoveThinkable( ClientThinkHandle_t hThink )
|
||||
{
|
||||
if ( hThink == INVALID_THINK_HANDLE )
|
||||
return;
|
||||
|
||||
if ( m_bInThinkLoop )
|
||||
{
|
||||
// Queue up all changes
|
||||
int i = m_aChangeList.AddToTail();
|
||||
m_aChangeList[i].m_hEnt = INVALID_CLIENTENTITY_HANDLE;
|
||||
m_aChangeList[i].m_hThink = hThink;
|
||||
m_aChangeList[i].m_flNextTime = CLIENT_THINK_NEVER;
|
||||
return;
|
||||
}
|
||||
|
||||
ThinkEntry_t *pEntry = GetThinkEntry( hThink );
|
||||
IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( pEntry->m_hEnt );
|
||||
if ( pThink )
|
||||
{
|
||||
pThink->SetThinkHandle( INVALID_THINK_HANDLE );
|
||||
}
|
||||
m_ThinkEntries.Remove( (unsigned long)hThink );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Removes the thinkable from the list
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::RemoveThinkable( ClientEntityHandle_t hEnt )
|
||||
{
|
||||
IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( hEnt );
|
||||
if ( pThink )
|
||||
{
|
||||
ClientThinkHandle_t hThink = pThink->GetThinkHandle();
|
||||
if ( hThink != INVALID_THINK_HANDLE )
|
||||
{
|
||||
Assert( GetThinkEntry( hThink )->m_hEnt == hEnt );
|
||||
RemoveThinkable( hThink );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Performs the think function
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::PerformThinkFunction( ThinkEntry_t *pEntry, float flCurtime )
|
||||
{
|
||||
IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( pEntry->m_hEnt );
|
||||
if ( !pThink )
|
||||
{
|
||||
RemoveThinkable( pEntry->m_hEnt );
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
else if ( pEntry->m_flNextClientThink == FLT_MAX )
|
||||
{
|
||||
// This is an entity that doesn't need to think again; remove it
|
||||
RemoveThinkable( pEntry->m_hEnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( pEntry->m_flNextClientThink <= flCurtime );
|
||||
|
||||
// Indicate we're not going to think again
|
||||
pEntry->m_flNextClientThink = FLT_MAX;
|
||||
|
||||
// NOTE: The Think function here could call SetNextClientThink
|
||||
// which would cause it to be readded into the list
|
||||
pThink->ClientThink();
|
||||
}
|
||||
|
||||
// Set this after the Think calls in case they look at LastClientThink
|
||||
pEntry->m_flLastClientThink = flCurtime;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add entity to frame think list
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::AddEntityToFrameThinkList( ThinkEntry_t *pEntry, bool bAlwaysChain, int &nCount, ThinkEntry_t **ppFrameThinkList )
|
||||
{
|
||||
// We may already have processed this owing to hierarchy rules
|
||||
if ( pEntry->m_nIterEnum == m_nIterEnum )
|
||||
return;
|
||||
|
||||
// If we're not thinking this frame, we don't have to worry about thinking after our parents
|
||||
bool bThinkThisInterval = ( pEntry->m_flNextClientThink == CLIENT_THINK_ALWAYS ) ||
|
||||
( pEntry->m_flNextClientThink <= gpGlobals->curtime );
|
||||
|
||||
// This logic makes it so that if a child thinks,
|
||||
// *all* hierarchical parents + grandparents will think first, even if some
|
||||
// of the parents don't need to think this frame
|
||||
if ( !bThinkThisInterval && !bAlwaysChain )
|
||||
return;
|
||||
|
||||
// Respect hierarchy
|
||||
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( pEntry->m_hEnt );
|
||||
if ( pEntity )
|
||||
{
|
||||
C_BaseEntity *pParent = pEntity->GetMoveParent();
|
||||
if ( pParent && (pParent->GetThinkHandle() != INVALID_THINK_HANDLE) )
|
||||
{
|
||||
ThinkEntry_t *pParentEntry = GetThinkEntry( pParent->GetThinkHandle() );
|
||||
AddEntityToFrameThinkList( pParentEntry, true, nCount, ppFrameThinkList );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bThinkThisInterval )
|
||||
return;
|
||||
|
||||
// Add the entry into the list
|
||||
pEntry->m_nIterEnum = m_nIterEnum;
|
||||
ppFrameThinkList[nCount++] = pEntry;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
if( !report_clientthinklist.GetBool() )
|
||||
{
|
||||
// Perform thinks on all entities that need it
|
||||
for ( int i = 0; i < nThinkCount; ++i )
|
||||
{
|
||||
PerformThinkFunction( ppThinkEntryList[i], gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CFastTimer fastTimer;
|
||||
|
||||
for ( int i = 0; i < nThinkCount; ++i )
|
||||
{
|
||||
fastTimer.Start();
|
||||
PerformThinkFunction( ppThinkEntryList[i], gpGlobals->curtime );
|
||||
fastTimer.End();
|
||||
|
||||
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( ppThinkEntryList[i]->m_hEnt );
|
||||
if ( pEntity )
|
||||
{
|
||||
Msg( "Entity(%d): %s - %f\n", pEntity->entindex(), pEntity->GetDebugName(), fastTimer.GetDuration().GetMillisecondsF() );
|
||||
}
|
||||
}
|
||||
|
||||
report_clientthinklist.SetValue( 0 );
|
||||
}
|
||||
|
||||
m_bInThinkLoop = false;
|
||||
|
||||
// Apply changes to the think list
|
||||
int nCount = m_aChangeList.Count();
|
||||
for ( int 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();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Queued-up entity deletion
|
||||
//-----------------------------------------------------------------------------
|
||||
void CClientThinkList::AddToDeleteList( ClientEntityHandle_t hEnt )
|
||||
{
|
||||
// Sanity check!
|
||||
Assert( hEnt != ClientEntityList().InvalidHandle() );
|
||||
if ( hEnt == ClientEntityList().InvalidHandle() )
|
||||
return;
|
||||
|
||||
// Check to see if entity is networkable -- don't let it release!
|
||||
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( hEnt );
|
||||
if ( pEntity )
|
||||
{
|
||||
// Check to see if the entity is already being removed!
|
||||
if ( pEntity->IsMarkedForDeletion() )
|
||||
return;
|
||||
|
||||
// Don't add networkable entities to delete list -- the server should
|
||||
// take care of this. The delete list is for client-side only entities.
|
||||
if ( !pEntity->GetClientNetworkable() )
|
||||
{
|
||||
m_aDeleteList.AddToTail( hEnt );
|
||||
pEntity->SetRemovalFlag( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CClientThinkList::RemoveFromDeleteList( ClientEntityHandle_t hEnt )
|
||||
{
|
||||
// Sanity check!
|
||||
Assert( hEnt != ClientEntityList().InvalidHandle() );
|
||||
if ( hEnt == ClientEntityList().InvalidHandle() )
|
||||
return;
|
||||
|
||||
int nSize = m_aDeleteList.Count();
|
||||
for ( int iHandle = 0; iHandle < nSize; ++iHandle )
|
||||
{
|
||||
if ( m_aDeleteList[iHandle] == hEnt )
|
||||
{
|
||||
m_aDeleteList[iHandle] = ClientEntityList().InvalidHandle();
|
||||
|
||||
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( hEnt );
|
||||
if ( pEntity )
|
||||
{
|
||||
pEntity->SetRemovalFlag( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CClientThinkList::CleanUpDeleteList()
|
||||
{
|
||||
int nThinkCount = m_aDeleteList.Count();
|
||||
for ( int iThink = 0; iThink < nThinkCount; ++iThink )
|
||||
{
|
||||
ClientEntityHandle_t handle = m_aDeleteList[iThink];
|
||||
if ( handle != ClientEntityList().InvalidHandle() )
|
||||
{
|
||||
C_BaseEntity *pEntity = ClientEntityList().GetBaseEntityFromHandle( handle );
|
||||
if ( pEntity )
|
||||
{
|
||||
pEntity->SetRemovalFlag( false );
|
||||
}
|
||||
|
||||
IClientThinkable *pThink = ClientEntityList().GetClientThinkableFromHandle( handle );
|
||||
if ( pThink )
|
||||
{
|
||||
pThink->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_aDeleteList.RemoveAll();
|
||||
}
|
||||
|
Reference in New Issue
Block a user