source-engine/engine/dt_send_eng.cpp

1046 lines
26 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <tier0/dbg.h>
#include <tier0/vprof.h>
#include <tier0/icommandline.h>
#include <commonmacros.h>
#include <checksum_crc.h>
#include "dt_send_eng.h"
#include "dt_encode.h"
#include "dt_instrumentation_server.h"
#include "dt_stack.h"
#include "common.h"
#include "packed_entity.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
extern int host_framecount;
CRC32_t SendTable_ComputeCRC();
class CSendTablePrecalc;
class CSendNode;
extern bool Sendprop_UsingDebugWatch();
// This stack doesn't actually call any proxies. It uses the CSendProxyRecipients to tell
// what can be sent to the specified client.
class CPropCullStack : public CDatatableStack
{
public:
CPropCullStack(
CSendTablePrecalc *pPrecalc,
int iClient,
const CSendProxyRecipients *pOldStateProxies,
const int nOldStateProxies,
const CSendProxyRecipients *pNewStateProxies,
const int nNewStateProxies
) :
CDatatableStack( pPrecalc, (unsigned char*)1, -1 ),
m_pOldStateProxies( pOldStateProxies ),
m_nOldStateProxies( nOldStateProxies ),
m_pNewStateProxies( pNewStateProxies ),
m_nNewStateProxies( nNewStateProxies )
{
m_pPrecalc = pPrecalc;
m_iClient = iClient;
}
inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
{
if ( pNode->GetDataTableProxyIndex() == DATATABLE_PROXY_INDEX_NOPROXY )
{
return (unsigned char*)1;
}
else
{
Assert( pNode->GetDataTableProxyIndex() < m_nNewStateProxies );
bool bCur = m_pNewStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
if ( m_pOldStateProxies )
{
Assert( pNode->GetDataTableProxyIndex() < m_nOldStateProxies );
bool bPrev = m_pOldStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
if ( bPrev != bCur )
{
if ( bPrev )
{
// Old state had the data and the new state doesn't.
return 0;
}
else
{
// Add ALL properties under this proxy because the previous state didn't have any of them.
for ( int i=0; i < pNode->m_nRecursiveProps; i++ )
{
if ( m_nNewProxyProps < ARRAYSIZE( m_NewProxyProps ) )
{
m_NewProxyProps[m_nNewProxyProps] = pNode->m_iFirstRecursiveProp + i;
}
else
{
Error( "CPropCullStack::CallPropProxy - overflowed m_nNewProxyProps" );
}
++m_nNewProxyProps;
}
// Tell the outer loop that writes to m_pOutProps not to write anything from this
// proxy since we just did.
return 0;
}
}
}
return (unsigned char*)bCur;
}
}
virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
{
// Remember where the game code pointed us for this datatable's data so
m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
{
CSendNode *pCurChild = pNode->GetChild( iChild );
unsigned char *pNewStructBase = NULL;
if ( pStructBase )
{
pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
}
RecurseAndCallProxies( pCurChild, pNewStructBase );
}
}
inline void AddProp( int iProp )
{
if ( m_nOutProps < m_nMaxOutProps )
{
m_pOutProps[m_nOutProps] = iProp;
}
else
{
Error( "CPropCullStack::AddProp - m_pOutProps overflowed" );
}
++m_nOutProps;
}
void CullPropsFromProxies( const int *pStartProps, int nStartProps, int *pOutProps, int nMaxOutProps )
{
m_nOutProps = 0;
m_pOutProps = pOutProps;
m_nMaxOutProps = nMaxOutProps;
m_nNewProxyProps = 0;
Init();
// This list will have any newly available props written into it. Write a sentinel at the end.
m_NewProxyProps[m_nNewProxyProps] = -1; // invalid marker
int *pCurNewProxyProp = m_NewProxyProps;
for ( int i=0; i < nStartProps; i++ )
{
int iProp = pStartProps[i];
// Fill in the gaps with any properties that are newly enabled by the proxies.
while ( (unsigned int) *pCurNewProxyProp < (unsigned int) iProp )
{
AddProp( *pCurNewProxyProp );
++pCurNewProxyProp;
}
// Now write this property's index if the proxies are allowing this property to be written.
if ( IsPropProxyValid( iProp ) )
{
AddProp( iProp );
// avoid that we add it twice.
if ( *pCurNewProxyProp == iProp )
++pCurNewProxyProp;
}
}
// add any remaining new proxy props
while ( (unsigned int) *pCurNewProxyProp < MAX_DATATABLE_PROPS )
{
AddProp( *pCurNewProxyProp );
++pCurNewProxyProp;
}
}
int GetNumOutProps()
{
return m_nOutProps;
}
private:
CSendTablePrecalc *m_pPrecalc;
int m_iClient; // Which client it's encoding out for.
const CSendProxyRecipients *m_pOldStateProxies;
const int m_nOldStateProxies;
const CSendProxyRecipients *m_pNewStateProxies;
const int m_nNewStateProxies;
// The output property list.
int *m_pOutProps;
int m_nMaxOutProps;
int m_nOutProps;
int m_NewProxyProps[MAX_DATATABLE_PROPS+1];
int m_nNewProxyProps;
};
// ----------------------------------------------------------------------------- //
// CEncodeInfo
// Used by SendTable_Encode.
// ----------------------------------------------------------------------------- //
class CEncodeInfo : public CServerDatatableStack
{
public:
CEncodeInfo( CSendTablePrecalc *pPrecalc, unsigned char *pStructBase, int objectID, bf_write *pOut ) :
CServerDatatableStack( pPrecalc, pStructBase, objectID ),
m_DeltaBitsWriter( pOut )
{
}
public:
CDeltaBitsWriter m_DeltaBitsWriter;
};
// ------------------------------------------------------------------------ //
// Globals.
// ------------------------------------------------------------------------ //
CUtlVector< SendTable* > g_SendTables;
CRC32_t g_SendTableCRC = 0;
// ------------------------------------------------------------------------ //
// SendTable functions.
// ------------------------------------------------------------------------ //
static bool s_debug_info_shown = false;
static int s_debug_bits_start = 0;
static inline void ShowEncodeDeltaWatchInfo(
const SendTable *pTable,
const SendProp *pProp,
bf_read &buffer,
const int objectID,
const int index )
{
if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) )
return;
static int lastframe = -1;
if ( host_framecount != lastframe )
{
lastframe = host_framecount;
ConDMsg( "delta entity: %i\n", objectID );
}
// work on copy of bitbuffer
bf_read copy = buffer;
s_debug_info_shown = true;
DecodeInfo info;
info.m_pStruct = NULL;
info.m_pData = NULL;
info.m_pRecvProp = NULL;
info.m_pProp = pProp;
info.m_pIn = &copy;
info.m_Value.m_Type = (SendPropType)pProp->m_Type;
int startBit = copy.GetNumBitsRead();
g_PropTypeFns[pProp->m_Type].Decode( &info );
int bits = copy.GetNumBitsRead() - startBit;
const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString();
const char *value = info.m_Value.ToString();
ConDMsg( "+ %s %s, %s, index %i, bits %i, value %s\n", pTable->GetName(), pProp->GetName(), type, index, bits, value );
}
static FORCEINLINE void SendTable_EncodeProp( CEncodeInfo * pInfo, unsigned long iProp )
{
// Call their proxy to get the property's value.
DVariant var;
const SendProp *pProp = pInfo->GetCurProp();
unsigned char *pStructBase = pInfo->GetCurStructBase();
pProp->GetProxyFn()(
pProp,
pStructBase,
pStructBase + pProp->GetOffset(),
&var,
0, // iElement
pInfo->GetObjectID()
);
// Write the index.
pInfo->m_DeltaBitsWriter.WritePropIndex( iProp );
g_PropTypeFns[pProp->m_Type].Encode(
pStructBase,
&var,
pProp,
pInfo->m_DeltaBitsWriter.GetBitBuf(),
pInfo->GetObjectID()
);
}
static bool SendTable_IsPropZero( CEncodeInfo *pInfo, unsigned long iProp )
{
const SendProp *pProp = pInfo->GetCurProp();
// Call their proxy to get the property's value.
DVariant var;
unsigned char *pBase = pInfo->GetCurStructBase();
pProp->GetProxyFn()(
pProp,
pBase,
pBase + pProp->GetOffset(),
&var,
0, // iElement
pInfo->GetObjectID()
);
return g_PropTypeFns[pProp->m_Type].IsZero( pBase, &var, pProp );
}
int SendTable_CullPropsFromProxies(
const SendTable *pTable,
const int *pStartProps,
int nStartProps,
const int iClient,
const CSendProxyRecipients *pOldStateProxies,
const int nOldStateProxies,
const CSendProxyRecipients *pNewStateProxies,
const int nNewStateProxies,
int *pOutProps,
int nMaxOutProps
)
{
Assert( !( nNewStateProxies && !pNewStateProxies ) );
CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies );
stack.CullPropsFromProxies( pStartProps, nStartProps, pOutProps, nMaxOutProps );
ErrorIfNot( stack.GetNumOutProps() <= nMaxOutProps, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) );
return stack.GetNumOutProps();
}
// compares properties and writes delta properties, it ignores reciepients
int SendTable_WriteAllDeltaProps(
const SendTable *pTable,
const void *pFromData,
const int nFromDataBits,
const void *pToData,
const int nToDataBits,
const int nObjectID,
bf_write *pBufOut )
{
// Calculate the delta props.
int deltaProps[MAX_DATATABLE_PROPS];
int nDeltaProps = SendTable_CalcDelta(
pTable,
pFromData,
nFromDataBits,
pToData,
nToDataBits,
deltaProps,
ARRAYSIZE( deltaProps ),
nObjectID );
// Write the properties.
SendTable_WritePropList(
pTable,
pToData, // object data
nToDataBits,
pBufOut, // output buffer
nObjectID,
deltaProps,
nDeltaProps
);
return nDeltaProps;
}
bool SendTable_Encode(
const SendTable *pTable,
const void *pStruct,
bf_write *pOut,
int objectID,
CUtlMemory<CSendProxyRecipients> *pRecipients,
bool bNonZeroOnly
)
{
CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) );
if ( pRecipients )
{
ErrorIfNot( pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") );
}
VPROF( "SendTable_Encode" );
CServerDTITimer timer( pTable, SERVERDTI_ENCODE );
// Setup all the info we'll be walking the tree with.
CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID, pOut );
info.m_pRecipients = pRecipients; // optional buffer to store the bits for which clients get what data.
info.Init();
int iNumProps = pPrecalc->GetNumProps();
for ( int iProp=0; iProp < iNumProps; iProp++ )
{
// skip if we don't have a valid prop proxy
if ( !info.IsPropProxyValid( iProp ) )
continue;
info.SeekToProp( iProp );
// skip empty prop if we only encode non-zero values
if ( bNonZeroOnly && SendTable_IsPropZero(&info, iProp) )
continue;
SendTable_EncodeProp( &info, iProp );
}
return !pOut->IsOverflowed();
}
void SendTable_WritePropList(
const SendTable *pTable,
const void *pState,
const int nBits,
bf_write *pOut,
const int objectID,
const int *pCheckProps,
const int nCheckProps
)
{
if ( nCheckProps == 0 )
{
// Write single final zero bit, signifying that there no changed properties
pOut->WriteOneBit( 0 );
return;
}
bool bDebugWatch = Sendprop_UsingDebugWatch();
s_debug_info_shown = false;
s_debug_bits_start = pOut->GetNumBitsWritten();
CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
CDeltaBitsWriter deltaBitsWriter( pOut );
bf_read inputBuffer( "SendTable_WritePropList->inputBuffer", pState, BitByte( nBits ), nBits );
CDeltaBitsReader inputBitsReader( &inputBuffer );
// Ok, they want to specify a small list of properties to check.
unsigned int iToProp = inputBitsReader.ReadNextPropIndex();
int i = 0;
while ( i < nCheckProps )
{
// Seek the 'to' state to the current property we want to check.
while ( iToProp < (unsigned int) pCheckProps[i] )
{
inputBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
iToProp = inputBitsReader.ReadNextPropIndex();
}
if ( iToProp >= MAX_DATATABLE_PROPS )
{
break;
}
if ( iToProp == (unsigned int) pCheckProps[i] )
{
const SendProp *pProp = pPrecalc->GetProp( iToProp );
// Show debug stuff.
if ( bDebugWatch )
{
ShowEncodeDeltaWatchInfo( pTable, pProp, inputBuffer, objectID, iToProp );
}
// See how many bits the data for this property takes up.
int nToStateBits;
int iStartBit = pOut->GetNumBitsWritten();
deltaBitsWriter.WritePropIndex( iToProp );
inputBitsReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pProp );
nToStateBits = pOut->GetNumBitsWritten() - iStartBit;
TRACE_PACKET( ( " Send Field (%s) = %d (%d bytes)\n", pProp->GetName(), nToStateBits, ( nToStateBits + 7 ) / 8 ) );
// Seek to the next prop.
iToProp = inputBitsReader.ReadNextPropIndex();
}
++i;
}
if ( s_debug_info_shown )
{
int bits = pOut->GetNumBitsWritten() - s_debug_bits_start;
ConDMsg( "= %i bits (%i bytes)\n", bits, Bits2Bytes(bits) );
}
inputBitsReader.ForceFinished(); // avoid a benign assert
}
int SendTable_CalcDelta(
const SendTable *pTable,
const void *pFromState,
const int nFromBits,
const void *pToState,
const int nToBits,
int *pDeltaProps,
int nMaxDeltaProps,
const int objectID
)
{
CServerDTITimer timer( pTable, SERVERDTI_CALCDELTA );
int *pDeltaPropsBase = pDeltaProps;
int *pDeltaPropsEnd = pDeltaProps + nMaxDeltaProps;
VPROF( "SendTable_CalcDelta" );
// Trivial reject.
//if ( CompareBitArrays( pFromState, pToState, nFromBits, nToBits ) )
//{
// return 0;
//}
CSendTablePrecalc* pPrecalc = pTable->m_pPrecalc;
bf_read toBits( "SendTable_CalcDelta/toBits", pToState, BitByte(nToBits), nToBits );
CDeltaBitsReader toBitsReader( &toBits );
unsigned int iToProp = toBitsReader.ReadNextPropIndex();
if ( pFromState )
{
bf_read fromBits( "SendTable_CalcDelta/fromBits", pFromState, BitByte(nFromBits), nFromBits );
CDeltaBitsReader fromBitsReader( &fromBits );
unsigned int iFromProp = fromBitsReader.ReadNextPropIndex();
for ( ; iToProp < MAX_DATATABLE_PROPS; iToProp = toBitsReader.ReadNextPropIndex() )
{
Assert( (int)iToProp >= 0 );
// Skip any properties in the from state that aren't in the to state.
while ( iFromProp < iToProp )
{
fromBitsReader.SkipPropData( pPrecalc->GetProp( iFromProp ) );
iFromProp = fromBitsReader.ReadNextPropIndex();
}
if ( iFromProp == iToProp )
{
// The property is in both states, so compare them and write the index
// if the states are different.
if ( fromBitsReader.ComparePropData( &toBitsReader, pPrecalc->GetProp( iToProp ) ) )
{
*pDeltaProps++ = iToProp;
if ( pDeltaProps >= pDeltaPropsEnd )
{
break;
}
}
// Seek to the next property.
iFromProp = fromBitsReader.ReadNextPropIndex();
}
else
{
// Only the 'to' state has this property, so just skip its data and register a change.
toBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
*pDeltaProps++ = iToProp;
if ( pDeltaProps >= pDeltaPropsEnd )
{
break;
}
}
}
Assert( iToProp == ~0u );
fromBitsReader.ForceFinished();
}
else
{
for ( ; iToProp != (uint)-1; iToProp = toBitsReader.ReadNextPropIndex() )
{
Assert( (int)iToProp >= 0 && iToProp < MAX_DATATABLE_PROPS );
const SendProp *pProp = pPrecalc->GetProp( iToProp );
if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &toBits ) )
{
*pDeltaProps++ = iToProp;
if ( pDeltaProps >= pDeltaPropsEnd )
{
break;
}
}
}
}
// Return the # of properties that changed between 'from' and 'to'.
return pDeltaProps - pDeltaPropsBase;
}
bool SendTable_WriteInfos( SendTable *pTable, bf_write *pBuf )
{
pBuf->WriteString( pTable->GetName() );
pBuf->WriteUBitLong( pTable->GetNumProps(), PROPINFOBITS_NUMPROPS );
// Send each property.
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
{
const SendProp *pProp = &pTable->m_pProps[iProp];
pBuf->WriteUBitLong( (unsigned int)pProp->m_Type, PROPINFOBITS_TYPE );
pBuf->WriteString( pProp->GetName() );
// we now have some flags that aren't networked so strip them off
unsigned int networkFlags = pProp->GetFlags() & ((1<<PROPINFOBITS_FLAGS)-1);
pBuf->WriteUBitLong( networkFlags, PROPINFOBITS_FLAGS );
if( pProp->m_Type == DPT_DataTable )
{
// Just write the name and it will be able to reuse the table with a matching name.
pBuf->WriteString( pProp->GetDataTable()->m_pNetTableName );
}
else
{
if ( pProp->IsExcludeProp() )
{
pBuf->WriteString( pProp->GetExcludeDTName() );
}
else if ( pProp->GetType() == DPT_Array )
{
pBuf->WriteUBitLong( pProp->GetNumElements(), PROPINFOBITS_NUMELEMENTS );
}
else
{
pBuf->WriteBitFloat( pProp->m_fLowValue );
pBuf->WriteBitFloat( pProp->m_fHighValue );
pBuf->WriteUBitLong( pProp->m_nBits, PROPINFOBITS_NUMBITS );
}
}
}
return !pBuf->IsOverflowed();
}
// Spits out warnings for invalid properties and forces property values to
// be in valid ranges for the encoders and decoders.
static void SendTable_Validate( CSendTablePrecalc *pPrecalc )
{
SendTable *pTable = pPrecalc->m_pSendTable;
for( int i=0; i < pTable->m_nProps; i++ )
{
SendProp *pProp = &pTable->m_pProps[i];
if ( pProp->GetArrayProp() )
{
if ( pProp->GetArrayProp()->GetType() == DPT_DataTable )
{
Error( "Invalid property: %s/%s (array of datatables) [on prop %d of %d (%s)].", pTable->m_pNetTableName, pProp->GetName(), i, pTable->m_nProps, pProp->GetArrayProp()->GetName() );
}
}
else
{
ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) );
}
// Check for 1-bit signed properties (their value doesn't get down to the client).
if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) )
{
DataTable_Warning("SendTable prop %s::%s is a 1-bit signed property. Use SPROP_UNSIGNED or the client will never receive a value.\n", pTable->m_pNetTableName, pProp->GetName());
}
}
for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
{
const SendProp *pProp = pPrecalc->GetProp( i );
if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
{
pTable->SetHasPropsEncodedAgainstTickcount( true );
break;
}
}
}
static void SendTable_CalcNextVectorElems( SendTable *pTable )
{
for ( int i=0; i < pTable->GetNumProps(); i++ )
{
SendProp *pProp = pTable->GetProp( i );
if ( pProp->GetType() == DPT_DataTable )
{
SendTable_CalcNextVectorElems( pProp->GetDataTable() );
}
else if ( pProp->GetOffset() < 0 )
{
pProp->SetOffset( -pProp->GetOffset() );
pProp->SetFlags( pProp->GetFlags() | SPROP_IS_A_VECTOR_ELEM );
}
}
}
static bool SendTable_InitTable( SendTable *pTable )
{
if( pTable->m_pPrecalc )
return true;
// Create the CSendTablePrecalc.
CSendTablePrecalc *pPrecalc = new CSendTablePrecalc;
pTable->m_pPrecalc = pPrecalc;
pPrecalc->m_pSendTable = pTable;
pTable->m_pPrecalc = pPrecalc;
SendTable_CalcNextVectorElems( pTable );
// Bind the instrumentation if -dti was specified.
pPrecalc->m_pDTITable = ServerDTI_HookTable( pTable );
// Setup its flat property array.
if ( !pPrecalc->SetupFlatPropertyArray() )
return false;
SendTable_Validate( pPrecalc );
return true;
}
static void SendTable_TermTable( SendTable *pTable )
{
if( !pTable->m_pPrecalc )
return;
delete pTable->m_pPrecalc;
Assert( !pTable->m_pPrecalc ); // Make sure it unbound itself.
}
int SendTable_GetNumFlatProps( SendTable *pSendTable )
{
CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
ErrorIfNot( pPrecalc,
("SendTable_GetNumFlatProps: missing pPrecalc.")
);
return pPrecalc->GetNumProps();
}
CRC32_t SendTable_CRCTable( CRC32_t &crc, SendTable *pTable )
{
CRC32_ProcessBuffer( &crc, (void *)pTable->m_pNetTableName, Q_strlen( pTable->m_pNetTableName) );
int nProps = LittleLong( pTable->m_nProps );
CRC32_ProcessBuffer( &crc, (void *)&nProps, sizeof( pTable->m_nProps ) );
// Send each property.
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
{
const SendProp *pProp = &pTable->m_pProps[iProp];
int type = LittleLong( pProp->m_Type );
CRC32_ProcessBuffer( &crc, (void *)&type, sizeof( type ) );
CRC32_ProcessBuffer( &crc, (void *)pProp->GetName() , Q_strlen( pProp->GetName() ) );
int flags = LittleLong( pProp->GetFlags() );
CRC32_ProcessBuffer( &crc, (void *)&flags, sizeof( flags ) );
if( pProp->m_Type == DPT_DataTable )
{
CRC32_ProcessBuffer( &crc, (void *)pProp->GetDataTable()->m_pNetTableName, Q_strlen( pProp->GetDataTable()->m_pNetTableName ) );
}
else
{
if ( pProp->IsExcludeProp() )
{
CRC32_ProcessBuffer( &crc, (void *)pProp->GetExcludeDTName(), Q_strlen( pProp->GetExcludeDTName() ) );
}
else if ( pProp->GetType() == DPT_Array )
{
int numelements = LittleLong( pProp->GetNumElements() );
CRC32_ProcessBuffer( &crc, (void *)&numelements, sizeof( numelements ) );
}
else
{
float lowvalue;
LittleFloat( &lowvalue, &pProp->m_fLowValue );
CRC32_ProcessBuffer( &crc, (void *)&lowvalue, sizeof( lowvalue ) );
float highvalue;
LittleFloat( &highvalue, &pProp->m_fHighValue );
CRC32_ProcessBuffer( &crc, (void *)&highvalue, sizeof( highvalue ) );
int bits = LittleLong( pProp->m_nBits );
CRC32_ProcessBuffer( &crc, (void *)&bits, sizeof( bits ) );
}
}
}
return crc;
}
void SendTable_PrintStats( void )
{
int numTables = 0;
int numFloats = 0;
int numStrings = 0;
int numArrays = 0;
int numInts = 0;
int numVecs = 0;
int numVecXYs = 0;
int numSubTables = 0;
int numSendProps = 0;
int numFlatProps = 0;
int numExcludeProps = 0;
for ( int i=0; i < g_SendTables.Count(); i++ )
{
SendTable *st = g_SendTables[i];
numTables++;
numSendProps += st->GetNumProps();
numFlatProps += st->m_pPrecalc->GetNumProps();
for ( int j=0; j < st->GetNumProps(); j++ )
{
SendProp* sp = st->GetProp( j );
if ( sp->IsExcludeProp() )
{
numExcludeProps++;
continue; // no real sendprops
}
if ( sp->IsInsideArray() )
continue;
switch( sp->GetType() )
{
case DPT_Int : numInts++; break;
case DPT_Float : numFloats++; break;
case DPT_Vector : numVecs++; break;
case DPT_VectorXY : numVecXYs++; break;
case DPT_String : numStrings++; break;
case DPT_Array : numArrays++; break;
case DPT_DataTable : numSubTables++; break;
}
}
}
Msg("Total Send Table stats\n");
Msg("Send Tables : %i\n", numTables );
Msg("Send Props : %i\n", numSendProps );
Msg("Flat Props : %i\n", numFlatProps );
Msg("Int Props : %i\n", numInts );
Msg("Float Props : %i\n", numFloats );
Msg("Vector Props : %i\n", numVecs );
Msg("VectorXY Props: %i\n", numVecXYs );
Msg("String Props : %i\n", numStrings );
Msg("Array Props : %i\n", numArrays );
Msg("Table Props : %i\n", numSubTables );
Msg("Exclu Props : %i\n", numExcludeProps );
}
bool SendTable_Init( SendTable **pTables, int nTables )
{
ErrorIfNot( g_SendTables.Count() == 0,
("SendTable_Init: called twice.")
);
// Initialize them all.
for ( int i=0; i < nTables; i++ )
{
if ( !SendTable_InitTable( pTables[i] ) )
return false;
}
// Store off the SendTable list.
g_SendTables.CopyArray( pTables, nTables );
g_SendTableCRC = SendTable_ComputeCRC( );
if ( CommandLine()->FindParm("-dti" ) )
{
SendTable_PrintStats();
}
return true;
}
void SendTable_Term()
{
// Term all the SendTables.
for ( int i=0; i < g_SendTables.Count(); i++ )
SendTable_TermTable( g_SendTables[i] );
// Clear the list of SendTables.
g_SendTables.Purge();
g_SendTableCRC = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Computes the crc for all sendtables for the data sent in the class/table definitions
// Output : CRC32_t
//-----------------------------------------------------------------------------
CRC32_t SendTable_ComputeCRC()
{
CRC32_t result;
CRC32_Init( &result );
// walk the tables and checksum them
int c = g_SendTables.Count();
for ( int i = 0 ; i < c; i++ )
{
SendTable *st = g_SendTables[ i ];
result = SendTable_CRCTable( result, st );
}
CRC32_Final( &result );
return result;
}
SendTable *SendTabe_GetTable(int index)
{
return g_SendTables[index];
}
int SendTable_GetNum()
{
return g_SendTables.Count();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CRC32_t
//-----------------------------------------------------------------------------
CRC32_t SendTable_GetCRC()
{
return g_SendTableCRC;
}
//-----------------------------------------------------------------------------
// Purpose: check integrity of an unpacked entity send table
//-----------------------------------------------------------------------------
bool SendTable_CheckIntegrity( SendTable *pTable, const void *pData, const int nDataBits )
{
#ifdef _DEBUG
if ( pData == NULL && nDataBits == 0 )
return true;
bf_read bfRead( "SendTable_CheckIntegrity", pData, Bits2Bytes(nDataBits), nDataBits );
CDeltaBitsReader bitsReader( &bfRead );
int iProp = -1;
int iLastProp = -1;
int nMaxProps = pTable->m_pPrecalc->GetNumProps();
int nPropCount = 0;
Assert( nMaxProps > 0 && nMaxProps < MAX_DATATABLE_PROPS );
while( -1 != (iProp = bitsReader.ReadNextPropIndex()) )
{
Assert( (iProp>=0) && (iProp<nMaxProps) );
// must be larger
Assert( iProp > iLastProp );
const SendProp *pProp = pTable->m_pPrecalc->GetProp( iProp );
Assert( pProp );
// ok check that SkipProp & IsEncodedZero read the same bit length
int iStartBit = bfRead.GetNumBitsRead();
g_PropTypeFns[ pProp->GetType() ].SkipProp( pProp, &bfRead );
int nLength = bfRead.GetNumBitsRead() - iStartBit;
Assert( nLength > 0 ); // a prop must have some bits
bfRead.Seek( iStartBit ); // read it again
g_PropTypeFns[ pProp->GetType() ].IsEncodedZero( pProp, &bfRead );
Assert( nLength == (bfRead.GetNumBitsRead() - iStartBit) );
nPropCount++;
iLastProp = iProp;
}
Assert( nPropCount <= nMaxProps );
Assert( bfRead.GetNumBytesLeft() < 4 );
Assert( !bfRead.IsOverflowed() );
#endif
return true;
}