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/dt_send_eng.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'engine/dt_send_eng.cpp')
| -rw-r--r-- | engine/dt_send_eng.cpp | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/engine/dt_send_eng.cpp b/engine/dt_send_eng.cpp new file mode 100644 index 0000000..f5d9db8 --- /dev/null +++ b/engine/dt_send_eng.cpp @@ -0,0 +1,1045 @@ +//========= 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 = © + 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; +} + + + + + |