//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "entitylist_base.h" #include "ihandleentity.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" enum { SERIAL_MASK = 0x7fff // the max value of a serial number, rolls back to 0 when it hits this limit }; void CEntInfo::ClearLinks() { m_pPrev = m_pNext = this; } CBaseEntityList::CEntInfoList::CEntInfoList() { m_pHead = NULL; m_pTail = NULL; } // NOTE: Cut from UtlFixedLinkedList<>, UNDONE: Find a way to share this code void CBaseEntityList::CEntInfoList::LinkBefore( CEntInfo *pBefore, CEntInfo *pElement ) { Assert( pElement ); // Unlink it if it's in the list at the moment Unlink(pElement); // The element *after* our newly linked one is the one we linked before. pElement->m_pNext = pBefore; if (pBefore == NULL) { // In this case, we're linking to the end of the list, so reset the tail pElement->m_pPrev = m_pTail; m_pTail = pElement; } else { // Here, we're not linking to the end. Set the prev pointer to point to // the element we're linking. Assert( IsInList(pBefore) ); pElement->m_pPrev = pBefore->m_pPrev; pBefore->m_pPrev = pElement; } // Reset the head if we linked to the head of the list if (pElement->m_pPrev == NULL) { m_pHead = pElement; } else { pElement->m_pPrev->m_pNext = pElement; } } void CBaseEntityList::CEntInfoList::LinkAfter( CEntInfo *pAfter, CEntInfo *pElement ) { Assert( pElement ); // Unlink it if it's in the list at the moment if ( IsInList(pElement) ) Unlink(pElement); // The element *before* our newly linked one is the one we linked after pElement->m_pPrev = pAfter; if (pAfter == NULL) { // In this case, we're linking to the head of the list, reset the head pElement->m_pNext = m_pHead; m_pHead = pElement; } else { // Here, we're not linking to the end. Set the next pointer to point to // the element we're linking. Assert( IsInList(pAfter) ); pElement->m_pNext = pAfter->m_pNext; pAfter->m_pNext = pElement; } // Reset the tail if we linked to the tail of the list if (pElement->m_pNext == NULL ) { m_pTail = pElement; } else { pElement->m_pNext->m_pPrev = pElement; } } void CBaseEntityList::CEntInfoList::Unlink( CEntInfo *pElement ) { if (IsInList(pElement)) { // If we're the first guy, reset the head // otherwise, make our previous node's next pointer = our next if ( pElement->m_pPrev ) { pElement->m_pPrev->m_pNext = pElement->m_pNext; } else { m_pHead = pElement->m_pNext; } // If we're the last guy, reset the tail // otherwise, make our next node's prev pointer = our prev if ( pElement->m_pNext ) { pElement->m_pNext->m_pPrev = pElement->m_pPrev; } else { m_pTail = pElement->m_pPrev; } // This marks this node as not in the list, // but not in the free list either pElement->ClearLinks(); } } bool CBaseEntityList::CEntInfoList::IsInList( CEntInfo *pElement ) { return pElement->m_pPrev != pElement; } CBaseEntityList::CBaseEntityList() { // These are not in any list (yet) int i; for ( i = 0; i < NUM_ENT_ENTRIES; i++ ) { m_EntPtrArray[i].ClearLinks(); m_EntPtrArray[i].m_SerialNumber = (rand()& SERIAL_MASK); // generate random starting serial number m_EntPtrArray[i].m_pEntity = NULL; } // make a free list of the non-networkable entities // Initially, all the slots are free. for ( i=MAX_EDICTS+1; i < NUM_ENT_ENTRIES; i++ ) { CEntInfo *pList = &m_EntPtrArray[i]; m_freeNonNetworkableList.AddToTail( pList ); } } CBaseEntityList::~CBaseEntityList() { CEntInfo *pList = m_activeList.Head(); while ( pList ) { CEntInfo *pNext = pList->m_pNext; RemoveEntityAtSlot( GetEntInfoIndex( pList ) ); pList = pNext; } } CBaseHandle CBaseEntityList::AddNetworkableEntity( IHandleEntity *pEnt, int index, int iForcedSerialNum ) { Assert( index >= 0 && index < MAX_EDICTS ); return AddEntityAtSlot( pEnt, index, iForcedSerialNum ); } CBaseHandle CBaseEntityList::AddNonNetworkableEntity( IHandleEntity *pEnt ) { // Find a slot for it. CEntInfo *pSlot = m_freeNonNetworkableList.Head(); if ( !pSlot ) { Warning( "CBaseEntityList::AddNonNetworkableEntity: no free slots!\n" ); AssertMsg( 0, ( "CBaseEntityList::AddNonNetworkableEntity: no free slots!\n" ) ); return CBaseHandle(); } // Move from the free list into the allocated list. m_freeNonNetworkableList.Unlink( pSlot ); int iSlot = GetEntInfoIndex( pSlot ); return AddEntityAtSlot( pEnt, iSlot, -1 ); } void CBaseEntityList::RemoveEntity( CBaseHandle handle ) { RemoveEntityAtSlot( handle.GetEntryIndex() ); } CBaseHandle CBaseEntityList::AddEntityAtSlot( IHandleEntity *pEnt, int iSlot, int iForcedSerialNum ) { // Init the CSerialEntity. CEntInfo *pSlot = &m_EntPtrArray[iSlot]; Assert( pSlot->m_pEntity == NULL ); pSlot->m_pEntity = pEnt; // Force the serial number (client-only)? if ( iForcedSerialNum != -1 ) { pSlot->m_SerialNumber = iForcedSerialNum; #if !defined( CLIENT_DLL ) // Only the client should force the serial numbers. Assert( false ); #endif } // Update our list of active entities. m_activeList.AddToTail( pSlot ); CBaseHandle retVal( iSlot, pSlot->m_SerialNumber ); // Tell the entity to store its handle. pEnt->SetRefEHandle( retVal ); // Notify any derived class. OnAddEntity( pEnt, retVal ); return retVal; } void CBaseEntityList::RemoveEntityAtSlot( int iSlot ) { Assert( iSlot >= 0 && iSlot < NUM_ENT_ENTRIES ); CEntInfo *pInfo = &m_EntPtrArray[iSlot]; if ( pInfo->m_pEntity ) { pInfo->m_pEntity->SetRefEHandle( INVALID_EHANDLE_INDEX ); // Notify the derived class that we're about to remove this entity. OnRemoveEntity( pInfo->m_pEntity, CBaseHandle( iSlot, pInfo->m_SerialNumber ) ); // Increment the serial # so ehandles go invalid. pInfo->m_pEntity = NULL; pInfo->m_SerialNumber = ( pInfo->m_SerialNumber+1)& SERIAL_MASK; m_activeList.Unlink( pInfo ); // Add the slot back to the free list if it's a non-networkable entity. if ( iSlot >= MAX_EDICTS ) { m_freeNonNetworkableList.AddToTail( pInfo ); } } } void CBaseEntityList::OnAddEntity( IHandleEntity *pEnt, CBaseHandle handle ) { } void CBaseEntityList::OnRemoveEntity( IHandleEntity *pEnt, CBaseHandle handle ) { }