//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include #include #include #include #include #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 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 *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<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 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; }