Game: properly implement entity list base classes

Properly implemented and now mostly useable in the SDK.
This commit is contained in:
Kawe Mazidjatari 2024-07-31 20:49:19 +02:00
parent 892f415425
commit 084f94aefd
9 changed files with 291 additions and 53 deletions

View File

@ -8,9 +8,6 @@
//===========================================================================// //===========================================================================//
#if !defined( CLIENTENTITYLIST_H ) #if !defined( CLIENTENTITYLIST_H )
#define CLIENTENTITYLIST_H #define CLIENTENTITYLIST_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h" #include "tier1/utlvector.h"
#include "tier1/utllinkedlist.h" #include "tier1/utllinkedlist.h"
@ -28,6 +25,10 @@ public:
virtual void OnEntityDeleted(C_BaseEntity* pEntity) {}; virtual void OnEntityDeleted(C_BaseEntity* pEntity) {};
}; };
//-----------------------------------------------------------------------------
// Purpose: a global list of all the entities in the game. All iteration through
// entities is done through this object.
//-----------------------------------------------------------------------------
class CClientEntityList : public C_BaseEntityList, public IClientEntityList class CClientEntityList : public C_BaseEntityList, public IClientEntityList
{ {
protected: protected:
@ -56,7 +57,6 @@ private:
// For fast iteration. // For fast iteration.
CUtlLinkedList<C_BaseEntity*, unsigned short> m_BaseEntities; CUtlLinkedList<C_BaseEntity*, unsigned short> m_BaseEntities;
}; };
COMPILE_TIME_ASSERT(sizeof(CClientEntityList) == 0x3800C0); COMPILE_TIME_ASSERT(sizeof(CClientEntityList) == 0x3800C0);
inline IClientEntityList* g_pClientEntityList = nullptr; inline IClientEntityList* g_pClientEntityList = nullptr;

View File

@ -18,6 +18,27 @@ public:
class C_BaseEntityList class C_BaseEntityList
{ {
public: public:
// Get an ehandle from a networkable entity's index (note: if there is no entity in that slot,
// then the ehandle will be invalid and produce NULL).
CBaseHandle GetNetworkableHandle(const int iEntity) const;
// ehandles use this in their Get() function to produce a pointer to the entity.
IHandleEntity* LookupEntity(const CBaseHandle& handle) const;
IHandleEntity* LookupEntityByNetworkIndex(const int edictIndex) const;
// Use these to iterate over all the entities.
CBaseHandle FirstHandle() const;
CBaseHandle NextHandle(const CBaseHandle& hEnt) const;
static CBaseHandle InvalidHandle();
const C_EntInfo* FirstEntInfo() const;
const C_EntInfo* NextEntInfo(const C_EntInfo* pInfo) const;
const C_EntInfo* GetEntInfoPtr(const CBaseHandle& hEnt) const;
const C_EntInfo* GetEntInfoPtrByIndex(const int index) const;
// Used by Foundry when an entity is respawned/edited.
// We force the new entity's ehandle to be the same so anyone pointing at it still gets a valid CBaseEntity out of their ehandle.
void ForceEntSerialNumber(const int iEntIndex, const int iSerialNumber);
// Overridables. // Overridables.
protected: protected:
@ -33,7 +54,11 @@ private:
class C_EntInfoList class C_EntInfoList
{ {
public: public:
C_EntInfoList(); C_EntInfoList()
{
m_pHead = NULL;
m_pTail = NULL;
}
const C_EntInfo* Head() const { return m_pHead; } const C_EntInfo* Head() const { return m_pHead; }
const C_EntInfo* Tail() const { return m_pTail; } const C_EntInfo* Tail() const { return m_pTail; }
@ -52,6 +77,8 @@ private:
C_EntInfo* m_pTail; C_EntInfo* m_pTail;
}; };
int GetEntInfoIndex(const C_EntInfo* pEntInfo) const;
// The first MAX_EDICTS entities are networkable. The rest are client-only. // The first MAX_EDICTS entities are networkable. The rest are client-only.
C_EntInfo m_EntPtrArray[NUM_ENT_ENTRIES]; C_EntInfo m_EntPtrArray[NUM_ENT_ENTRIES];
C_EntInfoList m_activeList; C_EntInfoList m_activeList;
@ -63,4 +90,101 @@ private:
ssize_t m_soundEntCount; ssize_t m_soundEntCount;
}; };
// ------------------------------------------------------------------------------------ //
// Inlines.
// ------------------------------------------------------------------------------------ //
inline int C_BaseEntityList::GetEntInfoIndex(const C_EntInfo* pEntInfo) const
{
Assert(pEntInfo);
const int index = (int)(pEntInfo - m_EntPtrArray);
Assert(index >= 0 && index < NUM_ENT_ENTRIES);
return index;
}
inline CBaseHandle C_BaseEntityList::GetNetworkableHandle(const int iEntity) const
{
Assert(iEntity >= 0 && iEntity < MAX_EDICTS);
if (m_EntPtrArray[iEntity].m_pEntity)
return CBaseHandle(iEntity, m_EntPtrArray[iEntity].m_SerialNumber);
else
return CBaseHandle();
}
inline IHandleEntity* C_BaseEntityList::LookupEntity(const CBaseHandle& handle) const
{
if (handle.m_Index == INVALID_EHANDLE_INDEX)
return NULL;
const C_EntInfo* pInfo = &m_EntPtrArray[handle.GetEntryIndex()];
if (pInfo->m_SerialNumber == handle.GetSerialNumber())
return pInfo->m_pEntity;
else
return NULL;
}
inline IHandleEntity* C_BaseEntityList::LookupEntityByNetworkIndex(const int edictIndex) const
{
// (Legacy support).
if (edictIndex < 0)
return NULL;
Assert(edictIndex < NUM_ENT_ENTRIES);
return m_EntPtrArray[edictIndex].m_pEntity;
}
inline CBaseHandle C_BaseEntityList::FirstHandle() const
{
if (!m_activeList.Head())
return INVALID_EHANDLE_INDEX;
const int index = GetEntInfoIndex(m_activeList.Head());
return CBaseHandle(index, m_EntPtrArray[index].m_SerialNumber);
}
inline CBaseHandle C_BaseEntityList::NextHandle(const CBaseHandle& hEnt) const
{
const int iSlot = hEnt.GetEntryIndex();
const C_EntInfo* pNext = m_EntPtrArray[iSlot].m_pNext;
if (!pNext)
return INVALID_EHANDLE_INDEX;
const int index = GetEntInfoIndex(pNext);
return CBaseHandle(index, m_EntPtrArray[index].m_SerialNumber);
}
inline CBaseHandle C_BaseEntityList::InvalidHandle()
{
return INVALID_EHANDLE_INDEX;
}
inline const C_EntInfo* C_BaseEntityList::FirstEntInfo() const
{
return m_activeList.Head();
}
inline const C_EntInfo* C_BaseEntityList::NextEntInfo(const C_EntInfo* pInfo) const
{
return pInfo->m_pNext;
}
inline const C_EntInfo* C_BaseEntityList::GetEntInfoPtr(const CBaseHandle& hEnt) const
{
const int iSlot = hEnt.GetEntryIndex();
return &m_EntPtrArray[iSlot];
}
inline const C_EntInfo* C_BaseEntityList::GetEntInfoPtrByIndex(const int index) const
{
return &m_EntPtrArray[index];
}
inline void C_BaseEntityList::ForceEntSerialNumber(const int iEntIndex, const int iSerialNumber)
{
m_EntPtrArray[iEntIndex].m_SerialNumber = iSerialNumber;
}
#endif // ENTITYLIST_CLIENTBASE_H #endif // ENTITYLIST_CLIENTBASE_H

View File

@ -6,12 +6,8 @@
#include "core/stdafx.h" #include "core/stdafx.h"
#include "entitylist.h" #include "entitylist.h"
IHandleEntity* LookupEntityByIndex(int iEntity) //-----------------------------------------------------------------------------
{ // Purpose: a global list of all the entities in the game. All iteration through
Assert(iEntity >= 0 && iEntity < NUM_ENT_ENTRIES); // Programmer error! // entities is done through this object.
//-----------------------------------------------------------------------------
IHandleEntity* pHandle = reinterpret_cast<IHandleEntity*>(*&g_pEntityList[6 * iEntity]); CGlobalEntityList* g_serverEntityList = nullptr;
return pHandle; // !TODO: implement CBaseEntityList properly.
}
CEntInfo** g_pEntityList = nullptr;

View File

@ -37,13 +37,9 @@ private:
bool m_bClearingEntities; bool m_bClearingEntities;
CUtlVector<IEntityListener*> m_entityListeners; CUtlVector<IEntityListener*> m_entityListeners;
}; };
COMPILE_TIME_ASSERT(sizeof(CGlobalEntityList) == 0x380088);
extern CGlobalEntityList* g_serverEntityList;
IHandleEntity* LookupEntityByIndex(int iEntity);
extern CEntInfo** g_pEntityList;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -51,14 +47,13 @@ class VServerEntityList : public IDetour
{ {
virtual void GetAdr(void) const virtual void GetAdr(void) const
{ {
LogVarAdr("g_serverEntityList", g_pEntityList); LogVarAdr("g_serverEntityList", g_serverEntityList);
} }
virtual void GetFun(void) const { } virtual void GetFun(void) const { }
virtual void GetVar(void) const virtual void GetVar(void) const
{ {
void* CBaseEntity__GetBaseEntity; g_GameDll.FindPatternSIMD("48 8D 0D ?? ?? ?? ?? 66 0F 7F 05 ?? ?? ?? ?? 44 89 0D").
g_GameDll.FindPatternSIMD("8B 91 ?? ?? ?? ?? 83 FA FF 74 1F 0F B7 C2 48 8D 0D ?? ?? ?? ?? C1 EA 10 48 8D 04 40 48 03 C0 39 54 C1 08 75 05 48 8B 04 C1 C3 33 C0 C3 CC CC CC 48 8B 41 30").GetPtr(CBaseEntity__GetBaseEntity); ResolveRelativeAddressSelf(3, 7).ResolveRelativeAddressSelf(3, 7).GetPtr(g_serverEntityList);
g_pEntityList = CMemory(CBaseEntity__GetBaseEntity).FindPattern("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast<CEntInfo**>();
} }
virtual void GetCon(void) const { } virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const { } virtual void Detour(const bool bAttach) const { }

View File

@ -21,6 +21,27 @@ public:
class CBaseEntityList class CBaseEntityList
{ {
public: public:
// Get an ehandle from a networkable entity's index (note: if there is no entity in that slot,
// then the ehandle will be invalid and produce NULL).
CBaseHandle GetNetworkableHandle(const int iEntity) const;
// ehandles use this in their Get() function to produce a pointer to the entity.
IHandleEntity* LookupEntity(const CBaseHandle& handle) const;
IHandleEntity* LookupEntityByNetworkIndex(const int edictIndex) const;
// Use these to iterate over all the entities.
CBaseHandle FirstHandle() const;
CBaseHandle NextHandle(const CBaseHandle& hEnt) const;
static CBaseHandle InvalidHandle();
const CEntInfo* FirstEntInfo() const;
const CEntInfo* NextEntInfo(const CEntInfo* pInfo) const;
const CEntInfo* GetEntInfoPtr(const CBaseHandle& hEnt) const;
const CEntInfo* GetEntInfoPtrByIndex(const int index) const;
// Used by Foundry when an entity is respawned/edited.
// We force the new entity's ehandle to be the same so anyone pointing at it still gets a valid CBaseEntity out of their ehandle.
void ForceEntSerialNumber(const int iEntIndex, const int iSerialNumber);
// Overridables. // Overridables.
protected: protected:
@ -36,7 +57,11 @@ private:
class CEntInfoList class CEntInfoList
{ {
public: public:
CEntInfoList(); CEntInfoList()
{
m_pHead = NULL;
m_pTail = NULL;
}
const CEntInfo* Head() const { return m_pHead; } const CEntInfo* Head() const { return m_pHead; }
const CEntInfo* Tail() const { return m_pTail; } const CEntInfo* Tail() const { return m_pTail; }
@ -55,6 +80,8 @@ private:
CEntInfo* m_pTail; CEntInfo* m_pTail;
}; };
int GetEntInfoIndex(const CEntInfo* pEntInfo) const;
// The first MAX_EDICTS entities are networkable. The rest are server-only. // The first MAX_EDICTS entities are networkable. The rest are server-only.
CEntInfo m_EntPtrArray[NUM_ENT_ENTRIES]; CEntInfo m_EntPtrArray[NUM_ENT_ENTRIES];
CEntInfoList m_activeList; CEntInfoList m_activeList;
@ -66,4 +93,101 @@ private:
ssize_t m_soundEntCount; ssize_t m_soundEntCount;
}; };
// ------------------------------------------------------------------------------------ //
// Inlines.
// ------------------------------------------------------------------------------------ //
inline int CBaseEntityList::GetEntInfoIndex(const CEntInfo* pEntInfo) const
{
Assert(pEntInfo);
const int index = (int)(pEntInfo - m_EntPtrArray);
Assert(index >= 0 && index < NUM_ENT_ENTRIES);
return index;
}
inline CBaseHandle CBaseEntityList::GetNetworkableHandle(const int iEntity) const
{
Assert(iEntity >= 0 && iEntity < MAX_EDICTS);
if (m_EntPtrArray[iEntity].m_pEntity)
return CBaseHandle(iEntity, m_EntPtrArray[iEntity].m_SerialNumber);
else
return CBaseHandle();
}
inline IHandleEntity* CBaseEntityList::LookupEntity(const CBaseHandle& handle) const
{
if (handle.m_Index == INVALID_EHANDLE_INDEX)
return NULL;
const CEntInfo* pInfo = &m_EntPtrArray[handle.GetEntryIndex()];
if (pInfo->m_SerialNumber == handle.GetSerialNumber())
return pInfo->m_pEntity;
else
return NULL;
}
inline IHandleEntity* CBaseEntityList::LookupEntityByNetworkIndex(const int edictIndex) const
{
// (Legacy support).
if (edictIndex < 0)
return NULL;
Assert(edictIndex < NUM_ENT_ENTRIES);
return m_EntPtrArray[edictIndex].m_pEntity;
}
inline CBaseHandle CBaseEntityList::FirstHandle() const
{
if (!m_activeList.Head())
return INVALID_EHANDLE_INDEX;
const int index = GetEntInfoIndex(m_activeList.Head());
return CBaseHandle(index, m_EntPtrArray[index].m_SerialNumber);
}
inline CBaseHandle CBaseEntityList::NextHandle(const CBaseHandle& hEnt) const
{
const int iSlot = hEnt.GetEntryIndex();
const CEntInfo* pNext = m_EntPtrArray[iSlot].m_pNext;
if (!pNext)
return INVALID_EHANDLE_INDEX;
const int index = GetEntInfoIndex(pNext);
return CBaseHandle(index, m_EntPtrArray[index].m_SerialNumber);
}
inline CBaseHandle CBaseEntityList::InvalidHandle()
{
return INVALID_EHANDLE_INDEX;
}
inline const CEntInfo* CBaseEntityList::FirstEntInfo() const
{
return m_activeList.Head();
}
inline const CEntInfo* CBaseEntityList::NextEntInfo(const CEntInfo* pInfo) const
{
return pInfo->m_pNext;
}
inline const CEntInfo* CBaseEntityList::GetEntInfoPtr(const CBaseHandle& hEnt) const
{
int iSlot = hEnt.GetEntryIndex();
return &m_EntPtrArray[iSlot];
}
inline const CEntInfo* CBaseEntityList::GetEntInfoPtrByIndex(const int index) const
{
return &m_EntPtrArray[index];
}
inline void CBaseEntityList::ForceEntSerialNumber(const int iEntIndex, const int iSerialNumber)
{
m_EntPtrArray[iEntIndex].m_SerialNumber = iSerialNumber;
}
#endif // ENTITYLIST_SERVERBASE_H #endif // ENTITYLIST_SERVERBASE_H

View File

@ -118,8 +118,8 @@ void __fastcall CServerGameDLL::OnReceivedSayTextMessage(void* thisptr, int send
void DrawServerHitbox(int iEntity) void DrawServerHitbox(int iEntity)
{ {
IHandleEntity* pEntity = LookupEntityByIndex(iEntity); const CEntInfo* pInfo = g_serverEntityList->GetEntInfoPtrByIndex(iEntity);
CBaseAnimating* pAnimating = dynamic_cast<CBaseAnimating*>(pEntity); CBaseAnimating* pAnimating = dynamic_cast<CBaseAnimating*>(pInfo->m_pEntity);
if (pAnimating) if (pAnimating)
{ {

View File

@ -120,7 +120,8 @@ inline const CHandle<CBaseEntity> &variant_t::Entity( void ) const
if ( fieldType == FIELD_EHANDLE ) if ( fieldType == FIELD_EHANDLE )
return eVal; return eVal;
static const CHandle<CBaseEntity> hNull( INVALID_EHANDLE ); static CHandle<CBaseEntity> hNull;
hNull.Set(NULL);
return(hNull); return(hNull);
} }

View File

@ -53,8 +53,8 @@ public:
CHandle(); CHandle();
CHandle( int iEntry, int iSerialNumber ); CHandle( int iEntry, int iSerialNumber );
/*implicit*/ CHandle( T *pVal ); CHandle( const CBaseHandle &handle );
/*implicit*/ CHandle( INVALID_EHANDLE_tag ); CHandle( T *pVal );
// NOTE: The following two constructor functions are not type-safe, and can allow creating a // NOTE: The following two constructor functions are not type-safe, and can allow creating a
// CHandle<T> that doesn't actually point to an object of type T. // CHandle<T> that doesn't actually point to an object of type T.
@ -90,18 +90,19 @@ inline CHandle<T>::CHandle()
{ {
} }
template<class T>
inline CHandle<T>::CHandle( INVALID_EHANDLE_tag )
: CBaseHandle( INVALID_EHANDLE )
{
}
template<class T> template<class T>
inline CHandle<T>::CHandle( int iEntry, int iSerialNumber ) inline CHandle<T>::CHandle( int iEntry, int iSerialNumber )
{ {
Init( iEntry, iSerialNumber ); Init( iEntry, iSerialNumber );
} }
template<class T>
CHandle<T>::CHandle( const CBaseHandle &handle )
: CBaseHandle( handle )
{
}
template<class T> template<class T>
inline CHandle<T>::CHandle( T *pObj ) inline CHandle<T>::CHandle( T *pObj )
: CBaseHandle( INVALID_EHANDLE ) : CBaseHandle( INVALID_EHANDLE )

View File

@ -22,19 +22,15 @@ class IHandleEntity;
// CBaseHandle. // CBaseHandle.
// -------------------------------------------------------------------------------------------------- // // -------------------------------------------------------------------------------------------------- //
enum INVALID_EHANDLE_tag
{
INVALID_EHANDLE
};
class CBaseHandle class CBaseHandle
{ {
friend class CBaseEntityList; friend class CBaseEntityList;
friend class C_BaseEntityList;
public: public:
CBaseHandle(); CBaseHandle();
CBaseHandle( INVALID_EHANDLE_tag ); CBaseHandle( unsigned long value );
CBaseHandle( const CBaseHandle &other ); CBaseHandle( const CBaseHandle &other );
explicit CBaseHandle( IHandleEntity* pHandleObj ); explicit CBaseHandle( IHandleEntity* pHandleObj );
CBaseHandle( int iEntry, int iSerialNumber ); CBaseHandle( int iEntry, int iSerialNumber );
@ -89,11 +85,12 @@ inline CBaseHandle::CBaseHandle()
m_Index = INVALID_EHANDLE_INDEX; m_Index = INVALID_EHANDLE_INDEX;
} }
inline CBaseHandle::CBaseHandle( INVALID_EHANDLE_tag ) inline CBaseHandle::CBaseHandle( unsigned long value )
{ {
m_Index = INVALID_EHANDLE_INDEX; m_Index = value;
} }
inline CBaseHandle::CBaseHandle( const CBaseHandle &other ) inline CBaseHandle::CBaseHandle( const CBaseHandle &other )
{ {
m_Index = other.m_Index; m_Index = other.m_Index;
@ -138,7 +135,7 @@ inline int CBaseHandle::GetEntryIndex() const
{ {
// There is a hack here: due to a bug in the original implementation of the // There is a hack here: due to a bug in the original implementation of the
// entity handle system, an attempt to look up an invalid entity index in // entity handle system, an attempt to look up an invalid entity index in
// certain cirumstances might fall through to the the mask operation below. // certain circumstances might fall through to the mask operation below.
// This would mask an invalid index to be in fact a lookup of entity number // This would mask an invalid index to be in fact a lookup of entity number
// NUM_ENT_ENTRIES, so invalid ent indexes end up actually looking up the // NUM_ENT_ENTRIES, so invalid ent indexes end up actually looking up the
// last slot in the entities array. Since this slot is always empty, the // last slot in the entities array. Since this slot is always empty, the
@ -150,7 +147,7 @@ inline int CBaseHandle::GetEntryIndex() const
// retains the prior (bug-submarining) behavior. // retains the prior (bug-submarining) behavior.
if ( !IsValid() ) if ( !IsValid() )
return NUM_ENT_ENTRIES-1; return NUM_ENT_ENTRIES-1;
return m_Index & ENT_ENTRY_MASK; return m_Index;
} }
inline int CBaseHandle::GetSerialNumber() const inline int CBaseHandle::GetSerialNumber() const
@ -193,12 +190,12 @@ inline bool CBaseHandle::operator <( const CBaseHandle &other ) const
// uint32 otherIndex = (pEntity) ? pEntity->GetRefEHandle().m_Index : INVALID_EHANDLE_INDEX; // uint32 otherIndex = (pEntity) ? pEntity->GetRefEHandle().m_Index : INVALID_EHANDLE_INDEX;
// return m_Index < otherIndex; // return m_Index < otherIndex;
//} //}
//
inline const CBaseHandle& CBaseHandle::operator=( const IHandleEntity *pEntity ) //inline const CBaseHandle& CBaseHandle::operator=( const IHandleEntity *pEntity )
{ //{
return Set( pEntity ); // return Set( pEntity );
} //}
//
//inline const CBaseHandle& CBaseHandle::Set( const IHandleEntity *pEntity ) //inline const CBaseHandle& CBaseHandle::Set( const IHandleEntity *pEntity )
//{ //{
// if ( pEntity ) // if ( pEntity )