You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
8.9 KiB
293 lines
8.9 KiB
//===================== 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
|