csgo-2018-source/engine/serializedentity.h

294 lines
8.9 KiB
C
Raw Normal View History

2021-07-25 12:11:47 +08:00
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
//
//
//
//==================================================================================================
#ifndef SERIALIZEDENTITY_H
#define SERIALIZEDENTITY_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmap.h"
#include "tier1/utlvector.h"
#include "dt.h"
#include "iclientnetworkable.h"
#include "ents_shared.h"
typedef int CFieldPath; // It's just an index into the flattened SendProp list!!!
//#define PARANOID_SERIALIZEDENTITY
class CSerializedEntity
{
public:
CSerializedEntity();
~CSerializedEntity();
//gets the number of fields contained within this serialized entity
int GetFieldCount() const { return m_nFieldCount; }
//determines the total number of bits used to hold all the field data
uint32 GetFieldDataBitCount() const { return m_nFieldDataBits; }
//determines if this entity is in a packed state. If so, it must be cleared and reallocated to change the size of any of the fields
bool IsPacked() const { return ( m_nNumReservedFields == knPacked ); }
//given an index, this will return the 'path' or identifier for that field
CFieldPath GetFieldPath( int nIndex ) const { Assert( nIndex < m_nFieldCount ); return m_pFields[ nIndex ]; }
void SetFieldPath( int nIndex, CFieldPath path ) { Assert( nIndex < m_nFieldCount ); m_pFields[ nIndex ] = path; }
//given a field index, this will return the starting bit within the field data for that field
uint32 GetFieldDataBitOffset( int nIndex ) const { Assert( nIndex < m_nFieldCount ); return m_pFieldDataOffsets[ nIndex ]; }
uint32 GetFieldDataBitEndOffset( int nIndex ) const { Assert( nIndex < m_nFieldCount ); return ( nIndex + 1 < m_nFieldCount ) ? m_pFieldDataOffsets[ nIndex + 1 ] : m_nFieldDataBits; }
void SetFieldDataBitOffset( int nIndex, uint32 nOffset ) { Assert( nIndex < m_nFieldCount ); m_pFieldDataOffsets[ nIndex ] = nOffset; }
//access to the direct memory buffers for those that need direct access
uint8* GetFieldData() { return m_pFieldData; }
const uint8* GetFieldData() const { return m_pFieldData; }
short* GetFieldPaths() { return m_pFields; }
const short* GetFieldPaths() const { return m_pFields; }
uint32* GetFieldDataBitOffsets() { return m_pFieldDataOffsets; }
const uint32* GetFieldDataBitOffsets() const { return m_pFieldDataOffsets; }
//given another serialized entity, this will just copy over the contents of the other entity, making its own copy
void Copy( const CSerializedEntity &other );
//given another serialized entity, this will handle swapping the contents of this one with the other serialized entity
void Swap( CSerializedEntity& other );
//clears all the contents of this serialized entity
void Clear();
//this sets up the memory within this serialized entity for the specified number of fields and total data size. This is useful
//for in place writing of results to avoid intermediate allocations, but care should be taken that the entity is already cleared
//prior to this call
void SetupPackMemory( int nNumFields, int nDataBits );
//given a field index, this will return all the information associated with it
void GetField( int nFieldIndex, CFieldPath &path, int *pnDataOffset, int *pnNextDataOffset ) const
{
path = GetFieldPath( nFieldIndex );
*pnDataOffset = GetFieldDataBitOffset( nFieldIndex );
*pnNextDataOffset = GetFieldDataBitEndOffset( nFieldIndex );
}
//---------------------------------------------------------------
//the following are only valid on unpacked serialized entities. When done with adding data, they should be packed with their field data
//called to reserve an amount of memory for the path and offset information
void ReservePathAndOffsetMemory( uint32 nNumElements );
//adds a path and offset to the list of fields in this set
void AddPathAndOffset( const CFieldPath &path, int nStartBit )
{
//inlined for performance when packing snapshots for networking (called for EVERY property)
Assert( !IsPacked() );
if ( m_nFieldCount == m_nNumReservedFields )
Grow();
m_pFields[m_nFieldCount] = path;
m_pFieldDataOffsets[m_nFieldCount] = nStartBit;
++m_nFieldCount;
}
//called to read the path and offset information form the field path, adding them to the list
bool ReadFieldPaths( bf_read *pBuf, CUtlVector< int > *pvecFieldPathBits = NULL ); // pvecFieldPathBits needed for DTI modes
//called to seal a dynamic list, packing it into a compact memory block along with its actual value data
void PackWithFieldData( void *pData, int nDataBits );
void PackWithFieldData( bf_read &buf, int nDataBits );
//---------------------------------------------------------------
// Sets up reading from the m_pFieldData
void StartReading( bf_read& bitReader ) const;
void StartWriting( bf_write& bitWriter );
//given an index (assumed in range), this will determine the size in bits of that field's data
int GetFieldDataSizeInBits( int nField ) const { return GetFieldDataBitEndOffset( nField ) - GetFieldDataBitOffset( nField ); }
static void DumpMemInfo();
bool operator ==( const CSerializedEntity &other ) const
{
if ( this == &other )
return true;
if ( m_nFieldCount != other.m_nFieldCount )
return false;
if ( m_nFieldDataBits != other.m_nFieldDataBits )
return false;
if ( V_memcmp( m_pFieldData, other.m_pFieldData, Bits2Bytes( m_nFieldDataBits ) ) )
return false;
for ( int i = 0; i < m_nFieldCount; ++i )
{
if ( m_pFields[ i ] != other.m_pFields[ i ] )
return false;
}
return true;
}
#ifndef PARANOID_SERIALIZEDENTITY
// Hack to make these accessible to helper classes/functions when using PARANOID_SERIALIZEDENTITY
private:
#endif
//constant used to designate when this entity is in a packed state or not
static const uint16 knPacked = (uint16)0xFFFF;
//the total number of fields that we have
uint16 m_nFieldCount;
//this is the internal count of how many fields we have preallocated, should always be >= field count. This will be knPacked if we are in a packed format
uint16 m_nNumReservedFields;
//the number of bits for our data
uint32 m_nFieldDataBits;
//the following fields are laid out in a memory block in the following order when packed, so only the first should be freed
short *m_pFields;
uint32 *m_pFieldDataOffsets;
uint8 *m_pFieldData;
//memory tracking information for development builds
#ifdef PARANOID_SERIALIZEDENTITY
char const *m_File;
int m_Line;
#endif
void Grow();
void Pack( short *pFields, uint32 *pFieldDataOffsets, int fieldCount, uint32 nFieldDataBits, uint8 *pFieldData );
CSerializedEntity( const CSerializedEntity &other );
DISALLOW_OPERATOR_EQUAL( CSerializedEntity );
DECLARE_FIXEDSIZE_ALLOCATOR_MT( CSerializedEntity );
};
class CSerializedEntityFieldIterator
{
public:
CSerializedEntityFieldIterator( CSerializedEntity *pEntity ) :
m_pEntity( pEntity )
, m_entityFieldCount( pEntity ? pEntity->GetFieldCount() : 0 )
, m_nFieldIndex( -1 )
, m_pCurrent( NULL )
{
m_Sentinel = PROP_SENTINEL;
}
const CFieldPath &First()
{
m_nFieldIndex = 0;
Update();
return GetField();
}
const CFieldPath *FirstPtr()
{
m_nFieldIndex = 0;
Update();
return GetFieldPtr();
}
const CFieldPath &Prev()
{
--m_nFieldIndex;
Update();
return GetField();
}
const CFieldPath *PrevPtr()
{
--m_nFieldIndex;
Update();
return GetFieldPtr();
}
const CFieldPath &Next()
{
++m_nFieldIndex;
Update();
return GetField();
}
const CFieldPath *NextPtr()
{
++m_nFieldIndex;
Update();
return GetFieldPtr();
}
bool IsValid() const
{
// Use unsigned math so there is only one comparison.
return (unsigned)m_nFieldIndex < (unsigned)m_entityFieldCount;
}
int GetIndex() const
{
return m_nFieldIndex;
}
CFieldPath SetIndex( int nFieldIndex )
{
m_nFieldIndex = nFieldIndex;
Update();
return GetField();
}
const CFieldPath &GetField() const
{
return *m_pCurrent;
}
const CFieldPath *GetFieldPtr() const
{
return m_pCurrent;
}
int GetOffset() const
{
return m_nOffset;
}
int GetLength() const
{
return m_nNextOffset - m_nOffset;
}
int GetNextOffset() const
{
return m_nNextOffset;
}
private:
void Update()
{
if ( !IsValid() )
{
Assert( m_Sentinel == PROP_SENTINEL );
m_pCurrent = &m_Sentinel;
m_nOffset = m_nNextOffset = -1;
}
else
{
m_pCurrent = &m_Current;
m_pEntity->GetField( m_nFieldIndex, m_Current, &m_nOffset, &m_nNextOffset );
}
}
CSerializedEntity * const m_pEntity;
// Cache the entity field count to avoid extra branches and pointer dereferences in IsValid()
const int m_entityFieldCount;
int m_nFieldIndex;
CFieldPath m_Sentinel;
CFieldPath *m_pCurrent;
CFieldPath m_Current;
int m_nOffset;
int m_nNextOffset;
};
#endif // SERIALIZEDENTITY_H