diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/sv_framesnapshot.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/sv_framesnapshot.cpp')
| -rw-r--r-- | engine/sv_framesnapshot.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/engine/sv_framesnapshot.cpp b/engine/sv_framesnapshot.cpp new file mode 100644 index 0000000..3e9f37c --- /dev/null +++ b/engine/sv_framesnapshot.cpp @@ -0,0 +1,508 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#include "server_pch.h" +#include <utllinkedlist.h> + +#include "hltvserver.h" +#if defined( REPLAY_ENABLED ) +#include "replayserver.h" +#endif +#include "framesnapshot.h" +#include "sys_dll.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DEFINE_FIXEDSIZE_ALLOCATOR( CFrameSnapshot, 64, 64 ); + + +static ConVar sv_creationtickcheck( "sv_creationtickcheck", "1", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Do extended check for encoding of timestamps against tickcount" ); +extern CGlobalVars g_ServerGlobalVariables; + +// Expose interface +static CFrameSnapshotManager g_FrameSnapshotManager; +CFrameSnapshotManager *framesnapshotmanager = &g_FrameSnapshotManager; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFrameSnapshotManager::CFrameSnapshotManager( void ) : m_PackedEntitiesPool( MAX_EDICTS / 16, CUtlMemoryPool::GROW_SLOW ) +{ + COMPILE_TIME_ASSERT( INVALID_PACKED_ENTITY_HANDLE == 0 ); + Q_memset( m_pPackedData, 0x00, MAX_EDICTS * sizeof(PackedEntityHandle_t) ); + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFrameSnapshotManager::~CFrameSnapshotManager( void ) +{ + AssertMsg1( m_FrameSnapshots.Count() == 0 || IsInErrorExit(), "Expected m_FrameSnapshots to be empty. It had %i items.", m_FrameSnapshots.Count() ); + + // TODO: This assert has been failing. HenryG says it's a valid assert and that we're probably leaking memory. + AssertMsg1( m_PackedEntitiesPool.Count() == 0 || IsInErrorExit(), "Expected m_PackedEntitiesPool to be empty. It had %i items.", m_PackedEntitiesPool.Count() ); +} + +//----------------------------------------------------------------------------- +// Called when a level change happens +//----------------------------------------------------------------------------- + +void CFrameSnapshotManager::LevelChanged() +{ + // Clear all lists... + Assert( m_FrameSnapshots.Count() == 0 ); + + // Release the most recent snapshot... + m_PackedEntityCache.RemoveAll(); + COMPILE_TIME_ASSERT( INVALID_PACKED_ENTITY_HANDLE == 0 ); + Q_memset( m_pPackedData, 0x00, MAX_EDICTS * sizeof(PackedEntityHandle_t) ); +} + +CFrameSnapshot* CFrameSnapshotManager::NextSnapshot( const CFrameSnapshot *pSnapshot ) +{ + if ( !pSnapshot || ((unsigned short)pSnapshot->m_ListIndex == m_FrameSnapshots.InvalidIndex()) ) + return NULL; + + int next = m_FrameSnapshots.Next(pSnapshot->m_ListIndex); + + if ( next == m_FrameSnapshots.InvalidIndex() ) + return NULL; + + // return next element in list + return m_FrameSnapshots[ next ]; +} + +CFrameSnapshot* CFrameSnapshotManager::CreateEmptySnapshot( int tickcount, int maxEntities ) +{ + CFrameSnapshot *snap = new CFrameSnapshot; + snap->AddReference(); + snap->m_nTickCount = tickcount; + snap->m_nNumEntities = maxEntities; + snap->m_nValidEntities = 0; + snap->m_pValidEntities = NULL; + snap->m_pHLTVEntityData = NULL; + snap->m_pReplayEntityData = NULL; + snap->m_pEntities = new CFrameSnapshotEntry[maxEntities]; + + CFrameSnapshotEntry *entry = snap->m_pEntities; + + // clear entries + for ( int i=0; i < maxEntities; i++) + { + entry->m_pClass = NULL; + entry->m_nSerialNumber = -1; + entry->m_pPackedData = INVALID_PACKED_ENTITY_HANDLE; + entry++; + } + + snap->m_ListIndex = m_FrameSnapshots.AddToTail( snap ); + return snap; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : framenumber - +//----------------------------------------------------------------------------- +CFrameSnapshot* CFrameSnapshotManager::TakeTickSnapshot( int tickcount ) +{ + unsigned short nValidEntities[MAX_EDICTS]; + + CFrameSnapshot *snap = CreateEmptySnapshot( tickcount, sv.num_edicts ); + + int maxclients = sv.GetClientCount(); + + CFrameSnapshotEntry *entry = snap->m_pEntities - 1; + edict_t *edict= sv.edicts - 1; + + // Build the snapshot. + for ( int i = 0; i < sv.num_edicts; i++ ) + { + edict++; + entry++; + + IServerUnknown *pUnk = edict->GetUnknown(); + + if ( !pUnk ) + continue; + + if ( edict->IsFree() ) + continue; + + // We don't want entities from inactive clients in the fullpack, + if ( i > 0 && i <= maxclients ) + { + // this edict is a client + if ( !sv.GetClient(i-1)->IsActive() ) + continue; + } + + // entity exists and is not marked as 'free' + Assert( edict->m_NetworkSerialNumber != -1 ); + Assert( edict->GetNetworkable() ); + Assert( edict->GetNetworkable()->GetServerClass() ); + + entry->m_nSerialNumber = edict->m_NetworkSerialNumber; + entry->m_pClass = edict->GetNetworkable()->GetServerClass(); + nValidEntities[snap->m_nValidEntities++] = i; + } + + // create dynamic valid entities array and copy indices + snap->m_pValidEntities = new unsigned short[snap->m_nValidEntities]; + Q_memcpy( snap->m_pValidEntities, nValidEntities, snap->m_nValidEntities * sizeof(unsigned short) ); + + if ( hltv && hltv->IsActive() ) + { + snap->m_pHLTVEntityData = new CHLTVEntityData[snap->m_nValidEntities]; + Q_memset( snap->m_pHLTVEntityData, 0, snap->m_nValidEntities * sizeof(CHLTVEntityData) ); + } + +#if defined( REPLAY_ENABLED ) + if ( replay && replay->IsActive() ) + { + snap->m_pReplayEntityData = new CReplayEntityData[snap->m_nValidEntities]; + Q_memset( snap->m_pReplayEntityData, 0, snap->m_nValidEntities * sizeof(CReplayEntityData) ); + } +#endif + + snap->m_iExplicitDeleteSlots.CopyArray( m_iExplicitDeleteSlots.Base(), m_iExplicitDeleteSlots.Count() ); + m_iExplicitDeleteSlots.Purge(); + + return snap; +} + +//----------------------------------------------------------------------------- +// Cleans up packed entity data +//----------------------------------------------------------------------------- + +void CFrameSnapshotManager::DeleteFrameSnapshot( CFrameSnapshot* pSnapshot ) +{ + // Decrement reference counts of all packed entities + for (int i = 0; i < pSnapshot->m_nNumEntities; ++i) + { + if ( pSnapshot->m_pEntities[i].m_pPackedData != INVALID_PACKED_ENTITY_HANDLE ) + { + RemoveEntityReference( pSnapshot->m_pEntities[i].m_pPackedData ); + } + } + + m_FrameSnapshots.Remove( pSnapshot->m_ListIndex ); + delete pSnapshot; +} + +void CFrameSnapshotManager::RemoveEntityReference( PackedEntityHandle_t handle ) +{ + Assert( handle != INVALID_PACKED_ENTITY_HANDLE ); + + PackedEntity *packedEntity = reinterpret_cast< PackedEntity * >( handle ); + + if ( --packedEntity->m_ReferenceCount <= 0) + { + AUTO_LOCK( m_WriteMutex ); + + m_PackedEntitiesPool.Free( packedEntity ); + + // if we have a uncompression cache, remove reference too + FOR_EACH_VEC( m_PackedEntityCache, i ) + { + UnpackedDataCache_t &pdc = m_PackedEntityCache[i]; + if ( pdc.pEntity == packedEntity ) + { + pdc.pEntity = NULL; + pdc.counter = 0; + break; + } + } + } +} + +void CFrameSnapshotManager::AddEntityReference( PackedEntityHandle_t handle ) +{ + Assert( handle != INVALID_PACKED_ENTITY_HANDLE ); + reinterpret_cast< PackedEntity * >( handle )->m_ReferenceCount++; +} + +void CFrameSnapshotManager::AddExplicitDelete( int iSlot ) +{ + AUTO_LOCK( m_WriteMutex ); + + if ( m_iExplicitDeleteSlots.Find(iSlot) == m_iExplicitDeleteSlots.InvalidIndex() ) + { + m_iExplicitDeleteSlots.AddToTail( iSlot ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the "basis" for encoding m_flAnimTime, m_flSimulationTime has changed +// since the time this entity was packed to the time we're trying to re-use the packing. +//----------------------------------------------------------------------------- +bool CFrameSnapshotManager::ShouldForceRepack( CFrameSnapshot* pSnapshot, int entity, PackedEntityHandle_t handle ) +{ + if ( sv_creationtickcheck.GetBool() ) + { + PackedEntity *pe = reinterpret_cast< PackedEntity * >( handle ); + Assert( pe ); + if ( pe && pe->ShouldCheckCreationTick() ) + { + int nCurrentNetworkBase = g_ServerGlobalVariables.GetNetworkBase( pSnapshot->m_nTickCount, entity ); + int nPackedEntityNetworkBase = g_ServerGlobalVariables.GetNetworkBase( pe->GetSnapshotCreationTick(), entity ); + if ( nCurrentNetworkBase != nPackedEntityNetworkBase ) + { + return true; + } + } + } + + return false; +} + +bool CFrameSnapshotManager::UsePreviouslySentPacket( CFrameSnapshot* pSnapshot, + int entity, int entSerialNumber ) +{ + PackedEntityHandle_t handle = m_pPackedData[entity]; + if ( handle != INVALID_PACKED_ENTITY_HANDLE ) + { + // NOTE: We can't use the previously sent packet if there was a + // serial number change.... + if ( m_pSerialNumber[entity] == entSerialNumber ) + { + // Check if we need to re-pack entity due to encoding against gpGlobals->tickcount + if ( framesnapshotmanager->ShouldForceRepack( pSnapshot, entity, handle ) ) + { + return false; + } + + Assert( entity < pSnapshot->m_nNumEntities ); + pSnapshot->m_pEntities[entity].m_pPackedData = handle; + reinterpret_cast< PackedEntity * >( handle )->m_ReferenceCount++; + return true; + } + else + { + return false; + } + } + + return false; +} + + +PackedEntity* CFrameSnapshotManager::GetPreviouslySentPacket( int iEntity, int iSerialNumber ) +{ + PackedEntityHandle_t handle = m_pPackedData[iEntity]; + if ( handle != INVALID_PACKED_ENTITY_HANDLE ) + { + // NOTE: We can't use the previously sent packet if there was a + // serial number change.... + if ( m_pSerialNumber[iEntity] == iSerialNumber ) + { + return reinterpret_cast< PackedEntity * >( handle ); + } + else + { + return NULL; + } + } + + return NULL; +} + +CThreadFastMutex &CFrameSnapshotManager::GetMutex() +{ + return m_WriteMutex; +} + +//----------------------------------------------------------------------------- +// Returns the pack data for a particular entity for a particular snapshot +//----------------------------------------------------------------------------- + +PackedEntity* CFrameSnapshotManager::CreatePackedEntity( CFrameSnapshot* pSnapshot, int entity ) +{ + m_WriteMutex.Lock(); + PackedEntity *packedEntity = m_PackedEntitiesPool.Alloc(); + PackedEntityHandle_t handle = reinterpret_cast< PackedEntityHandle_t >( packedEntity ); + m_WriteMutex.Unlock(); + + Assert( entity < pSnapshot->m_nNumEntities ); + + // Referenced twice: in the mru + packedEntity->m_ReferenceCount = 2; + packedEntity->m_nEntityIndex = entity; + pSnapshot->m_pEntities[entity].m_pPackedData = handle; + + // Add a reference into the global list of last entity packets seen... + // and remove the reference to the last entity packet we saw + if (m_pPackedData[entity] != INVALID_PACKED_ENTITY_HANDLE ) + { + RemoveEntityReference( m_pPackedData[entity] ); + } + + m_pPackedData[entity] = handle; + m_pSerialNumber[entity] = pSnapshot->m_pEntities[entity].m_nSerialNumber; + + packedEntity->SetSnapshotCreationTick( pSnapshot->m_nTickCount ); + + return packedEntity; +} + +//----------------------------------------------------------------------------- +// Returns the pack data for a particular entity for a particular snapshot +//----------------------------------------------------------------------------- + +PackedEntity* CFrameSnapshotManager::GetPackedEntity( CFrameSnapshot* pSnapshot, int entity ) +{ + if ( !pSnapshot ) + return NULL; + + Assert( entity < pSnapshot->m_nNumEntities ); + + PackedEntityHandle_t index = pSnapshot->m_pEntities[entity].m_pPackedData; + + if ( index == INVALID_PACKED_ENTITY_HANDLE ) + return NULL; + + PackedEntity *packedEntity = reinterpret_cast< PackedEntity * >( index ); + Assert( packedEntity->m_nEntityIndex == entity ); + return packedEntity; +} + + + +// ------------------------------------------------------------------------------------------------ // +// purpose: lookup cache if we have an uncompressed version of this packed entity +// ------------------------------------------------------------------------------------------------ // +UnpackedDataCache_t *CFrameSnapshotManager::GetCachedUncompressedEntity( PackedEntity *packedEntity ) +{ + if ( m_PackedEntityCache.Count() == 0 ) + { + // ops, we have no cache yet, create one and reset counter + m_nPackedEntityCacheCounter = 0; + m_PackedEntityCache.SetCount( 128 ); + + FOR_EACH_VEC( m_PackedEntityCache, i ) + { + m_PackedEntityCache[i].pEntity = NULL; + m_PackedEntityCache[i].counter = 0; + } + } + + m_nPackedEntityCacheCounter++; + + // remember oldest cache entry + UnpackedDataCache_t *pdcOldest = NULL; + int oldestValue = m_nPackedEntityCacheCounter; + + + FOR_EACH_VEC( m_PackedEntityCache, i ) + { + UnpackedDataCache_t *pdc = &m_PackedEntityCache[i]; + + if ( pdc->pEntity == packedEntity ) + { + // hit, found it, update counter + pdc->counter = m_nPackedEntityCacheCounter; + return pdc; + } + + if( pdc->counter < oldestValue ) + { + oldestValue = pdc->counter; + pdcOldest = pdc; + } + } + + Assert ( pdcOldest ); + + // hmm, not in cache, clear & return oldest one + pdcOldest->counter = m_nPackedEntityCacheCounter; + pdcOldest->bits = -1; // important, this is the signal for the caller to fill this structure + pdcOldest->pEntity = packedEntity; + return pdcOldest; +} + + + + +// ------------------------------------------------------------------------------------------------ // +// CFrameSnapshot +// ------------------------------------------------------------------------------------------------ // + +#if defined( _DEBUG ) + int g_nAllocatedSnapshots = 0; +#endif + + +CFrameSnapshot::CFrameSnapshot() +{ + m_nTempEntities = 0; + m_pTempEntities = NULL; + m_pValidEntities = NULL; + m_nReferences = 0; +#if defined( _DEBUG ) + ++g_nAllocatedSnapshots; + Assert( g_nAllocatedSnapshots < 80000 ); // this probably would indicate a memory leak. +#endif +} + + +CFrameSnapshot::~CFrameSnapshot() +{ + delete [] m_pValidEntities; + delete [] m_pEntities; + + if ( m_pTempEntities ) + { + Assert( m_nTempEntities>0 ); + for (int i = 0; i < m_nTempEntities; i++ ) + { + delete m_pTempEntities[i]; + } + + delete [] m_pTempEntities; + } + + if ( m_pHLTVEntityData ) + { + delete [] m_pHLTVEntityData; + } + + if ( m_pReplayEntityData ) + { + delete [] m_pReplayEntityData; + } + Assert ( m_nReferences == 0 ); + +#if defined( _DEBUG ) + --g_nAllocatedSnapshots; + Assert( g_nAllocatedSnapshots >= 0 ); +#endif +} + + +void CFrameSnapshot::AddReference() +{ + Assert( m_nReferences < 0xFFFF ); + ++m_nReferences; +} + +void CFrameSnapshot::ReleaseReference() +{ + Assert( m_nReferences > 0 ); + + --m_nReferences; + if ( m_nReferences == 0 ) + { + g_FrameSnapshotManager.DeleteFrameSnapshot( this ); + } +} + +CFrameSnapshot* CFrameSnapshot::NextSnapshot() const +{ + return g_FrameSnapshotManager.NextSnapshot( this ); +} + + |