//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //===========================================================================// #if !defined( CLIENTENTITYLIST_H ) #define CLIENTENTITYLIST_H #ifdef _WIN32 #pragma once #endif #include "tier0/dbg.h" #include "icliententitylist.h" #include "iclientunknown.h" #include "utllinkedlist.h" #include "utlvector.h" #include "icliententityinternal.h" #include "ispatialpartition.h" #include "cdll_util.h" #include "entitylist_base.h" #include "utlmap.h" class C_Beam; class C_BaseViewModel; class C_BaseEntity; #define INPVS_YES 0x0001 // The entity thinks it's in the PVS. #define INPVS_THISFRAME 0x0002 // Accumulated as different views are rendered during the frame and used to notify the entity if // it is not in the PVS anymore (at the end of the frame). #define INPVS_NEEDSNOTIFY 0x0004 // The entity thinks it's in the PVS. class IClientEntityListener; abstract_class C_BaseEntityClassList { public: C_BaseEntityClassList(); ~C_BaseEntityClassList(); virtual void LevelShutdown() = 0; C_BaseEntityClassList *m_pNextClassList; }; template< class T > class C_EntityClassList : public C_BaseEntityClassList { public: virtual void LevelShutdown() { m_pClassList = NULL; } void Insert( T *pEntity ) { pEntity->m_pNext = m_pClassList; m_pClassList = pEntity; } void Remove( T *pEntity ) { T **pPrev = &m_pClassList; T *pCur = *pPrev; while ( pCur ) { if ( pCur == pEntity ) { *pPrev = pCur->m_pNext; return; } pPrev = &pCur->m_pNext; pCur = *pPrev; } } static T *m_pClassList; }; // Maximum size of entity list #define INVALID_CLIENTENTITY_HANDLE CBaseHandle( INVALID_EHANDLE_INDEX ) // // This is the IClientEntityList implemenation. It serves two functions: // // 1. It converts server entity indices into IClientNetworkables for the engine. // // 2. It provides a place to store IClientUnknowns and gives out ClientEntityHandle_t's // so they can be indexed and retreived. For example, this is how static props are referenced // by the spatial partition manager - it doesn't know what is being inserted, so it's // given ClientEntityHandle_t's, and the handlers for spatial partition callbacks can // use the client entity list to look them up and check for supported interfaces. // class CClientEntityList : public CBaseEntityList, public IClientEntityList { friend class C_BaseEntityIterator; friend class C_AllBaseEntityIterator; public: // Constructor, destructor CClientEntityList( void ); virtual ~CClientEntityList( void ); void Release(); // clears everything and releases entities // Implement IClientEntityList public: virtual IClientNetworkable* GetClientNetworkable( int entnum ); virtual IClientEntity* GetClientEntity( int entnum ); virtual int NumberOfEntities( bool bIncludeNonNetworkable = false ); virtual IClientUnknown* GetClientUnknownFromHandle( ClientEntityHandle_t hEnt ); virtual IClientNetworkable* GetClientNetworkableFromHandle( ClientEntityHandle_t hEnt ); virtual IClientEntity* GetClientEntityFromHandle( ClientEntityHandle_t hEnt ); virtual int GetHighestEntityIndex( void ); virtual void SetMaxEntities( int maxents ); virtual int GetMaxEntities( ); // CBaseEntityList overrides. protected: virtual void OnAddEntity( IHandleEntity *pEnt, CBaseHandle handle ); virtual void OnRemoveEntity( IHandleEntity *pEnt, CBaseHandle handle ); // Internal to client DLL. public: // All methods of accessing specialized IClientUnknown's go through here. IClientUnknown* GetListedEntity( int entnum ); // Simple wrappers for convenience.. C_BaseEntity* GetBaseEntity( int entnum ); ICollideable* GetCollideable( int entnum ); IClientRenderable* GetClientRenderableFromHandle( ClientEntityHandle_t hEnt ); C_BaseEntity* GetBaseEntityFromHandle( ClientEntityHandle_t hEnt ); ICollideable* GetCollideableFromHandle( ClientEntityHandle_t hEnt ); IClientThinkable* GetClientThinkableFromHandle( ClientEntityHandle_t hEnt ); // Convenience methods to convert between entindex + ClientEntityHandle_t ClientEntityHandle_t EntIndexToHandle( int entnum ); int HandleToEntIndex( ClientEntityHandle_t handle ); // Is a handle valid? bool IsHandleValid( ClientEntityHandle_t handle ) const; // For backwards compatibility... C_BaseEntity* GetEnt( int entnum ) { return GetBaseEntity( entnum ); } void RecomputeHighestEntityUsed( void ); // Use this to iterate over all the C_BaseEntities. C_BaseEntity* FirstBaseEntity() const; C_BaseEntity* NextBaseEntity( C_BaseEntity *pEnt ) const; class CPVSNotifyInfo { public: IPVSNotify *m_pNotify; IClientRenderable *m_pRenderable; unsigned char m_InPVSStatus; // Combination of the INPVS_ flags. unsigned short m_PVSNotifiersLink; // Into m_PVSNotifyInfos. }; // Get the list of all PVS notifiers. CUtlLinkedList& GetPVSNotifiers(); CUtlVector m_entityListeners; // add a class that gets notified of entity events void AddListenerEntity( IClientEntityListener *pListener ); void RemoveListenerEntity( IClientEntityListener *pListener ); void NotifyCreateEntity( C_BaseEntity *pEnt ); void NotifyRemoveEntity( C_BaseEntity *pEnt ); private: // Cached info for networked entities. struct EntityCacheInfo_t { // Cached off because GetClientNetworkable is called a *lot* IClientNetworkable *m_pNetworkable; unsigned short m_BaseEntitiesIndex; // Index into m_BaseEntities (or m_BaseEntities.InvalidIndex() if none). }; // Current count int m_iNumServerEnts; // Max allowed int m_iMaxServerEnts; int m_iNumClientNonNetworkable; // Current last used slot int m_iMaxUsedServerIndex; // This holds fast lookups for special edicts. EntityCacheInfo_t m_EntityCacheInfo[NUM_ENT_ENTRIES]; // For fast iteration. CUtlLinkedList m_BaseEntities; private: void AddPVSNotifier( IClientUnknown *pUnknown ); void RemovePVSNotifier( IClientUnknown *pUnknown ); // These entities want to know when they enter and leave the PVS (server entities // already can get the equivalent notification with NotifyShouldTransmit, but client // entities have to get it this way). CUtlLinkedList m_PVSNotifyInfos; CUtlMap m_PVSNotifierMap; // Maps IClientUnknowns to indices into m_PVSNotifyInfos. }; // Use this to iterate over *all* (even dormant) the C_BaseEntities in the client entity list. class C_AllBaseEntityIterator { public: C_AllBaseEntityIterator(); void Restart(); C_BaseEntity* Next(); // keep calling this until it returns null. private: unsigned short m_CurBaseEntity; }; class C_BaseEntityIterator { public: C_BaseEntityIterator(); void Restart(); C_BaseEntity* Next(); // keep calling this until it returns null. private: unsigned short m_CurBaseEntity; }; //----------------------------------------------------------------------------- // Inline methods //----------------------------------------------------------------------------- inline bool CClientEntityList::IsHandleValid( ClientEntityHandle_t handle ) const { return handle.Get() != 0; } inline IClientUnknown* CClientEntityList::GetListedEntity( int entnum ) { return (IClientUnknown*)LookupEntityByNetworkIndex( entnum ); } inline IClientUnknown* CClientEntityList::GetClientUnknownFromHandle( ClientEntityHandle_t hEnt ) { return (IClientUnknown*)LookupEntity( hEnt ); } inline CUtlLinkedList& CClientEntityList::GetPVSNotifiers() { return m_PVSNotifyInfos; } //----------------------------------------------------------------------------- // Convenience methods to convert between entindex + ClientEntityHandle_t //----------------------------------------------------------------------------- inline ClientEntityHandle_t CClientEntityList::EntIndexToHandle( int entnum ) { if ( entnum < -1 ) return INVALID_EHANDLE_INDEX; IClientUnknown *pUnk = GetListedEntity( entnum ); return pUnk ? pUnk->GetRefEHandle() : INVALID_EHANDLE_INDEX; } //----------------------------------------------------------------------------- // Returns the client entity list //----------------------------------------------------------------------------- extern CClientEntityList *cl_entitylist; inline CClientEntityList& ClientEntityList() { return *cl_entitylist; } // Implement this class and register with entlist to receive entity create/delete notification class IClientEntityListener { public: virtual void OnEntityCreated( C_BaseEntity *pEntity ) {}; //virtual void OnEntitySpawned( C_BaseEntity *pEntity ) {}; virtual void OnEntityDeleted( C_BaseEntity *pEntity ) {}; }; #endif // CLIENTENTITYLIST_H