Counter Strike : Global Offensive Source Code
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.

1850 lines
57 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <tier0/dbg.h>
  8. #include <tier0/vprof.h>
  9. #include <tier0/icommandline.h>
  10. #include <commonmacros.h>
  11. #include <checksum_crc.h>
  12. #include "dt_send_eng.h"
  13. #include "dt_encode.h"
  14. #include "dt_instrumentation_server.h"
  15. #include "dt_stack.h"
  16. #include "common.h"
  17. #include "packed_entity.h"
  18. #include "serializedentity.h"
  19. #include "edict.h"
  20. #include "netmessages.h"
  21. #include <algorithm>
  22. // Define this to get a report on which props are being sent frequently and
  23. // have high-numbered indices. Marking these with SPROP_CHANGES_OFTEN can
  24. // improve CPU and network efficiency.
  25. //#define PROP_SKIPPED_REPORT
  26. #ifdef PROP_SKIPPED_REPORT
  27. #include <map>
  28. #include <string>
  29. #endif
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include <tier0/memdbgon.h>
  32. extern int host_framecount;
  33. CRC32_t SendTable_ComputeCRC();
  34. #define CRC_SEND_TABLE_VERBOSE 0
  35. #if CRC_SEND_TABLE_VERBOSE
  36. #define CRCMSG DevMsg
  37. #else
  38. #define CRCMSG (void)
  39. #endif
  40. #define CRCMEMx( var, nByte ) ( unsigned int )( ( reinterpret_cast< unsigned char * >( &var ) )[nByte] )
  41. #define CRCMEM4( var ) CRCMEMx( var, 0 ), CRCMEMx( var, 1 ), CRCMEMx( var, 2 ), CRCMEMx( var, 3 )
  42. #define CRCFMT4 "%02X%02X%02X%02X"
  43. class CSendTablePrecalc;
  44. class CSendNode;
  45. // This stack doesn't actually call any proxies. It uses the CSendProxyRecipients to tell
  46. // what can be sent to the specified client.
  47. class CPropCullStack : public CDatatableStack
  48. {
  49. public:
  50. CPropCullStack(
  51. CSendTablePrecalc *pPrecalc,
  52. int iClient,
  53. const CSendProxyRecipients *pOldStateProxies,
  54. const int nOldStateProxies,
  55. const CSendProxyRecipients *pNewStateProxies,
  56. const int nNewStateProxies
  57. ) :
  58. CDatatableStack( pPrecalc, (unsigned char*)1, -1 ),
  59. m_pOldStateProxies( pOldStateProxies ),
  60. m_nOldStateProxies( nOldStateProxies ),
  61. m_pNewStateProxies( pNewStateProxies ),
  62. m_nNewStateProxies( nNewStateProxies )
  63. {
  64. m_pPrecalc = pPrecalc;
  65. m_iClient = iClient;
  66. }
  67. inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
  68. {
  69. if ( pNode->GetDataTableProxyIndex() == DATATABLE_PROXY_INDEX_NOPROXY )
  70. {
  71. return (unsigned char*)1;
  72. }
  73. else
  74. {
  75. Assert( pNode->GetDataTableProxyIndex() < m_nNewStateProxies );
  76. bool bCur = m_pNewStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
  77. if ( m_pOldStateProxies )
  78. {
  79. Assert( pNode->GetDataTableProxyIndex() < m_nOldStateProxies );
  80. bool bPrev = m_pOldStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
  81. if ( bPrev != bCur )
  82. {
  83. if ( bPrev )
  84. {
  85. // Old state had the data and the new state doesn't.
  86. return 0;
  87. }
  88. else
  89. {
  90. // Add ALL properties under this proxy because the previous state didn't have any of them.
  91. for ( int i=0; i < pNode->m_nRecursiveProps; i++ )
  92. {
  93. if ( m_nNewProxyProps < ARRAYSIZE( m_NewProxyProps ) )
  94. {
  95. m_NewProxyProps[m_nNewProxyProps] = pNode->m_iFirstRecursiveProp + i;
  96. }
  97. else
  98. {
  99. Error( "CPropCullStack::CallPropProxy - overflowed m_nNewProxyProps" );
  100. }
  101. ++m_nNewProxyProps;
  102. }
  103. // Tell the outer loop that writes to m_pOutProps not to write anything from this
  104. // proxy since we just did.
  105. return 0;
  106. }
  107. }
  108. }
  109. return (unsigned char*)bCur;
  110. }
  111. }
  112. virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
  113. {
  114. // Remember where the game code pointed us for this datatable's data so
  115. m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
  116. for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
  117. {
  118. CSendNode *pCurChild = pNode->GetChild( iChild );
  119. unsigned char *pNewStructBase = NULL;
  120. if ( pStructBase )
  121. {
  122. pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
  123. }
  124. RecurseAndCallProxies( pCurChild, pNewStructBase );
  125. }
  126. }
  127. // Replay: when client is in replay, we substitute the client index and cull props as if they were being sent to a regular HLTV client
  128. void CullPropsFromProxies( const CalcDeltaResultsList_t &startProps, CalcDeltaResultsList_t &outProps )
  129. {
  130. m_pOutProps = &outProps;
  131. m_nNewProxyProps = 0;
  132. Init( false, false );
  133. // This list will have any newly available props written into it. Write a sentinel at the end.
  134. m_NewProxyProps[m_nNewProxyProps] = PROP_SENTINEL;
  135. int *pCurNewProxyProp = m_NewProxyProps;
  136. for ( int i=0; i < startProps.Count(); i++ )
  137. {
  138. int iProp = startProps[i];
  139. // Fill in the gaps with any properties that are newly enabled by the proxies.
  140. while ( *pCurNewProxyProp < iProp )
  141. {
  142. m_pOutProps->AddToTail( *pCurNewProxyProp );
  143. ++pCurNewProxyProp;
  144. }
  145. // Now write this property's index if the proxies are allowing this property to be written.
  146. if ( IsPropProxyValid( iProp ) )
  147. {
  148. m_pOutProps->AddToTail( iProp );
  149. // avoid that we add it twice.
  150. if ( *pCurNewProxyProp == iProp )
  151. ++pCurNewProxyProp;
  152. }
  153. }
  154. // add any remaining new proxy props
  155. while ( *pCurNewProxyProp < PROP_SENTINEL )
  156. {
  157. m_pOutProps->AddToTail( *pCurNewProxyProp );
  158. ++pCurNewProxyProp;
  159. }
  160. }
  161. int GetNumOutProps()
  162. {
  163. return m_pOutProps->Count();
  164. }
  165. private:
  166. CSendTablePrecalc *m_pPrecalc;
  167. int m_iClient; // Which client it's encoding out for.
  168. const CSendProxyRecipients *m_pOldStateProxies;
  169. const int m_nOldStateProxies;
  170. const CSendProxyRecipients *m_pNewStateProxies;
  171. const int m_nNewStateProxies;
  172. // The output property list.
  173. CalcDeltaResultsList_t *m_pOutProps;
  174. int m_NewProxyProps[MAX_DATATABLE_PROPS+1];
  175. int m_nNewProxyProps;
  176. };
  177. // ----------------------------------------------------------------------------- //
  178. // CDeltaCalculator encapsulates part of the functionality for calculating deltas between entity states.
  179. //
  180. // To use it, initialize it with the from and to states, then walk through the 'to' properties using
  181. // SeekToNextToProp().
  182. //
  183. // At each 'to' prop, either call SkipToProp or CalcDelta
  184. // ----------------------------------------------------------------------------- //
  185. class CDeltaCalculator
  186. {
  187. public:
  188. CDeltaCalculator(
  189. CSendTablePrecalc *pPrecalc,
  190. const void *pFromState,
  191. const int nFromBits,
  192. const void *pToState,
  193. const int nToBits,
  194. int *pDeltaProps,
  195. int nMaxDeltaProps,
  196. const int objectID );
  197. ~CDeltaCalculator();
  198. // Returns the next prop index in the 'to' buffer. Returns PROP_SENTINEL if there are no more.
  199. // If a valid property index is returned, you MUST call PropSkip() or PropCalcDelta() after calling this.
  200. int SeekToNextProp();
  201. // Ignore the current 'to' prop.
  202. void PropSkip();
  203. // Seek the 'from' buffer up to the current property, determine if there is a delta, and
  204. // write the delta (and the property data) into the buffer if there is a delta.
  205. void PropCalcDelta();
  206. // check if current propert is non zero, if so put it into delta indices list
  207. void PropCalcNonZero();
  208. // Returns the number of deltas detected throughout the
  209. int GetNumDeltasDetected();
  210. private:
  211. CSendTablePrecalc *m_pPrecalc;
  212. bf_read m_bfFromState;
  213. bf_read m_bfToState;
  214. CDeltaBitsReader m_FromBitsReader;
  215. CDeltaBitsReader m_ToBitsReader;
  216. int m_ObjectID;
  217. // Current property indices.
  218. int m_iFromProp;
  219. int m_iToProp;
  220. // Bit position into m_bfToState where the current prop (m_iToProp) starts.
  221. int m_iToStateStart;
  222. // Output..
  223. int *m_pDeltaProps;
  224. int m_nMaxDeltaProps;
  225. int m_nDeltaProps;
  226. };
  227. CDeltaCalculator::CDeltaCalculator(
  228. CSendTablePrecalc *pPrecalc,
  229. const void *pFromState,
  230. const int nFromBits,
  231. const void *pToState,
  232. const int nToBits,
  233. int *pDeltaProps,
  234. int nMaxDeltaProps,
  235. const int objectID )
  236. : m_bfFromState( "CDeltaCalculator->m_bfFromState", pFromState, Bits2Bytes( nFromBits ), nFromBits ),
  237. m_FromBitsReader( &m_bfFromState ),
  238. m_bfToState( "CDeltaCalculator->m_bfToState", pToState, Bits2Bytes( nToBits ), nToBits ),
  239. m_ToBitsReader( &m_bfToState )
  240. {
  241. m_pPrecalc = pPrecalc;
  242. m_ObjectID = objectID;
  243. m_pDeltaProps = pDeltaProps;
  244. m_nMaxDeltaProps = nMaxDeltaProps;
  245. m_nDeltaProps = 0;
  246. // Walk through each property in the to state, looking for ones in the from
  247. // state that are missing or that are different.
  248. if ( pFromState)
  249. {
  250. m_iFromProp = NextProp( &m_FromBitsReader );
  251. }
  252. else
  253. {
  254. m_iFromProp = PROP_SENTINEL;
  255. }
  256. m_iToProp = -1;
  257. }
  258. CDeltaCalculator::~CDeltaCalculator()
  259. {
  260. // Make sure we didn't overflow.
  261. ErrorIfNot(
  262. m_nDeltaProps <= m_nMaxDeltaProps,
  263. ( "SendTable_CalcDelta: overflowed props %d max %d on datatable '%s'.", m_nDeltaProps, m_nMaxDeltaProps, m_pPrecalc->GetSendTable()->GetName() )
  264. );
  265. ErrorIfNot(
  266. !m_bfFromState.IsOverflowed(),
  267. ( "SendTable_CalcDelta: m_bfFromState overflowed %d max %d on datatable '%s'.", m_nDeltaProps, m_nMaxDeltaProps, m_pPrecalc->GetSendTable()->GetName() )
  268. );
  269. ErrorIfNot(
  270. !m_bfToState.IsOverflowed(),
  271. ( "SendTable_CalcDelta: m_bfToState overflowed %d max %d on datatable '%s'.", m_nDeltaProps, m_nMaxDeltaProps, m_pPrecalc->GetSendTable()->GetName() )
  272. );
  273. // We may not have read to the end of our input bits, but we don't care.
  274. m_FromBitsReader.ForceFinished();
  275. m_ToBitsReader.ForceFinished();
  276. }
  277. inline int CDeltaCalculator::SeekToNextProp()
  278. {
  279. m_iToProp = NextProp( &m_ToBitsReader );
  280. return m_iToProp;
  281. }
  282. inline void CDeltaCalculator::PropSkip()
  283. {
  284. Assert( m_iToProp != -1 );
  285. SkipPropData( &m_bfToState, m_pPrecalc->GetProp( m_iToProp ) );
  286. }
  287. void CDeltaCalculator::PropCalcNonZero()
  288. {
  289. const SendProp *pProp = m_pPrecalc->GetProp( m_iToProp );
  290. if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &m_bfToState ) )
  291. {
  292. // add non-zero property to index list
  293. if ( m_nDeltaProps < m_nMaxDeltaProps )
  294. {
  295. m_pDeltaProps[m_nDeltaProps] = m_iToProp;
  296. }
  297. ++m_nDeltaProps;
  298. }
  299. }
  300. void CDeltaCalculator::PropCalcDelta()
  301. {
  302. // Skip any properties in the from state that aren't in the to state.
  303. while ( m_iFromProp < m_iToProp )
  304. {
  305. SkipPropData( &m_bfFromState, m_pPrecalc->GetProp( m_iFromProp ) );
  306. m_iFromProp = NextProp( &m_FromBitsReader );
  307. }
  308. const SendProp *pProp = m_pPrecalc->GetProp( m_iToProp );
  309. int bChange = true;
  310. if ( m_iFromProp == m_iToProp )
  311. {
  312. // The property is in both states, so compare them and write the index
  313. // if the states are different.
  314. bChange = g_PropTypeFns[pProp->m_Type].CompareDeltas( pProp, &m_bfFromState, &m_bfToState );
  315. // Seek to the next properties.
  316. m_iFromProp = NextProp( &m_FromBitsReader );
  317. }
  318. else
  319. {
  320. // Only the 'to' state has this property, so just skip its data and register a change.
  321. SkipPropData( &m_bfToState, pProp );
  322. }
  323. if ( bChange )
  324. {
  325. // Write the property index.
  326. if ( m_nDeltaProps < m_nMaxDeltaProps )
  327. {
  328. m_pDeltaProps[m_nDeltaProps] = m_iToProp;
  329. m_nDeltaProps++;
  330. }
  331. }
  332. }
  333. inline int CDeltaCalculator::GetNumDeltasDetected()
  334. {
  335. return m_nDeltaProps;
  336. }
  337. // Returns true if bits are the same
  338. static bool CompareBits( bf_read &from, bf_read &to, int numbits )
  339. {
  340. unsigned int temp1, temp2;
  341. int remaining = numbits;
  342. while ( remaining > 0 )
  343. {
  344. int count = MIN( remaining, sizeof( unsigned int ) << 3 );
  345. temp1 = from.ReadUBitLong( count );
  346. temp2 = to.ReadUBitLong( count );
  347. if ( temp1 != temp2 )
  348. return false;
  349. remaining -= count;
  350. }
  351. return true;
  352. }
  353. // ----------------------------------------------------------------------------- //
  354. // CEncodeInfo
  355. // Used by SendTable_Encode.
  356. // ----------------------------------------------------------------------------- //
  357. class CEncodeInfo : public CServerDatatableStack
  358. {
  359. public:
  360. CEncodeInfo( CSendTablePrecalc *pPrecalc, unsigned char *pStructBase, int objectID ) :
  361. CServerDatatableStack( pPrecalc, pStructBase, objectID )
  362. {
  363. }
  364. public:
  365. CSerializedEntity *m_pEntity;
  366. bf_write *m_pOut;
  367. };
  368. // ------------------------------------------------------------------------ //
  369. // Globals.
  370. // ------------------------------------------------------------------------ //
  371. CUtlVector< SendTable* > g_SendTables;
  372. #if CRC_SEND_TABLE_VERBOSE
  373. CUtlVector< SendTable* > g_SendTablesReferenced;
  374. #endif
  375. CRC32_t g_SendTableCRC = 0;
  376. // ------------------------------------------------------------------------ //
  377. // SendTable functions.
  378. // ------------------------------------------------------------------------ //
  379. bool s_debug_info_shown = false;
  380. int s_debug_bits_start = 0;
  381. static inline void ShowEncodeDeltaWatchInfo(
  382. const SendTable *pTable,
  383. const SendProp *pProp,
  384. bf_read &buffer,
  385. const int objectID,
  386. const int index )
  387. {
  388. if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) )
  389. return;
  390. static int lastframe = -1;
  391. if ( host_framecount != lastframe )
  392. {
  393. lastframe = host_framecount;
  394. ConDMsg( "E: delta entity: %i %s\n", objectID, pTable->GetName() );
  395. }
  396. // work on copy of bitbuffer
  397. bf_read copy = buffer;
  398. s_debug_info_shown = true;
  399. DecodeInfo info;
  400. info.m_pStruct = NULL;
  401. info.m_pData = NULL;
  402. info.m_pRecvProp = NULL;
  403. info.m_pProp = pProp;
  404. info.m_pIn = &copy;
  405. info.m_ObjectID = objectID;
  406. info.m_Value.m_Type = (SendPropType)pProp->m_Type;
  407. int startBit = copy.GetNumBitsRead();
  408. g_PropTypeFns[pProp->m_Type].Decode( &info );
  409. int bits = copy.GetNumBitsRead() - startBit;
  410. const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString();
  411. const char *value = info.m_Value.ToString();
  412. ConDMsg( "+ %s %s, %s, index %i, bits %i, value %s\n", pTable->GetName(), pProp->GetName(), type, index, bits, value );
  413. }
  414. static inline void SendTable_EncodeProp( CEncodeInfo *pInfo, unsigned long iProp )
  415. {
  416. // Write the index.
  417. // Encode it.
  418. const int iStartPos = pInfo->m_pOut->GetNumBitsWritten();
  419. pInfo->m_pEntity->AddPathAndOffset( iProp, iStartPos );
  420. const SendProp *pProp = pInfo->GetCurProp();
  421. // Call their proxy to get the property's value.
  422. DVariant var;
  423. pProp->GetProxyFn()(
  424. pProp,
  425. pInfo->GetCurStructBase(),
  426. pInfo->GetCurStructBase() + pProp->GetOffset(),
  427. &var,
  428. 0, // iElement
  429. pInfo->GetObjectID()
  430. );
  431. g_PropTypeFns[pProp->m_Type].Encode(
  432. pInfo->GetCurStructBase(),
  433. &var,
  434. pProp,
  435. pInfo->m_pOut,
  436. pInfo->GetObjectID()
  437. );
  438. }
  439. static bool SendTable_IsPropZero( CEncodeInfo *pInfo, unsigned long iProp )
  440. {
  441. const SendProp *pProp = pInfo->GetCurProp();
  442. // Call their proxy to get the property's value.
  443. DVariant var;
  444. pProp->GetProxyFn()(
  445. pProp,
  446. pInfo->GetCurStructBase(),
  447. pInfo->GetCurStructBase() + pProp->GetOffset(),
  448. &var,
  449. 0, // iElement
  450. pInfo->GetObjectID()
  451. );
  452. return g_PropTypeFns[pProp->m_Type].IsZero( pInfo->GetCurStructBase(), &var, pProp );
  453. }
  454. int SendTable_CullPropsFromProxies(
  455. const SendTable *pTable,
  456. const CalcDeltaResultsList_t &startProps,
  457. const int iClient,
  458. const CSendProxyRecipients *pOldStateProxies,
  459. const int nOldStateProxies,
  460. const CSendProxyRecipients *pNewStateProxies,
  461. const int nNewStateProxies,
  462. CalcDeltaResultsList_t &outProps
  463. )
  464. {
  465. Assert( !( nNewStateProxies && !pNewStateProxies ) );
  466. CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies );
  467. stack.CullPropsFromProxies( startProps, outProps );
  468. ErrorIfNot( stack.GetNumOutProps() <= MAX_DATATABLE_PROPS, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) );
  469. return stack.GetNumOutProps();
  470. }
  471. // compares properties and writes delta properties, it ignores reciepients
  472. int SendTable_WriteAllDeltaProps(
  473. const SendTable *pTable,
  474. SerializedEntityHandle_t fromEntity, // Can be invalid
  475. SerializedEntityHandle_t toEntity,
  476. const int nObjectID,
  477. bf_write *pBufOut )
  478. {
  479. CalcDeltaResultsList_t deltaProps;
  480. SendTable_CalcDelta( pTable, fromEntity, toEntity, nObjectID, deltaProps );
  481. // Write all properties.
  482. SendTable_WritePropList(
  483. pTable,
  484. toEntity,
  485. pBufOut, // output buffer
  486. nObjectID,
  487. &deltaProps );
  488. return deltaProps.Count();
  489. }
  490. bool SendTable_Encode(
  491. const SendTable *pTable,
  492. SerializedEntityHandle_t handle,
  493. const void *pStruct,
  494. int objectID,
  495. CUtlMemory<CSendProxyRecipients> *pRecipients
  496. )
  497. {
  498. CSerializedEntity *pEntity = reinterpret_cast< CSerializedEntity * >( handle );
  499. Assert( pEntity );
  500. pEntity->Clear();
  501. CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
  502. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) );
  503. if ( pRecipients )
  504. {
  505. ErrorIfNot( pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") );
  506. }
  507. VPROF( "SendTable_Encode" );
  508. uint8 packedData[MAX_PACKEDENTITY_DATA];
  509. bf_write writeBuf( "CFlattenedSerializer::Encode writeBuf", packedData, sizeof( packedData ) );
  510. CServerDTITimer timer( pTable, SERVERDTI_ENCODE );
  511. // Setup all the info we'll be walking the tree with.
  512. CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID );
  513. info.m_pOut = &writeBuf;
  514. info.m_pEntity = pEntity;
  515. info.m_pRecipients = pRecipients; // optional buffer to store the bits for which clients get what data.
  516. info.Init( false, false );
  517. const int iNumProps = pPrecalc->GetNumProps();
  518. //reserve memory for our path and offset information in our entity to avoid a lot of needless resizes
  519. info.m_pEntity->ReservePathAndOffsetMemory( iNumProps );
  520. for ( int iProp=0; iProp < iNumProps; iProp++ )
  521. {
  522. // skip if we don't have a valid prop proxy
  523. if ( !info.IsPropProxyValid( iProp ) )
  524. continue;
  525. info.SeekToProp( iProp );
  526. SendTable_EncodeProp( &info, iProp );
  527. }
  528. pEntity->PackWithFieldData( writeBuf.GetBasePointer(), writeBuf.GetNumBitsWritten() );
  529. return !writeBuf.IsOverflowed();
  530. }
  531. #ifdef PROP_SKIPPED_REPORT
  532. static int s_skippedProps;
  533. static int s_frameCount;
  534. std::map<std::string, int> s_highProps;
  535. const int kFrameReportInterval = 128;
  536. const int kMinPropOffset = 200;
  537. const int kReportLength = 10;
  538. const bool bReportDetails = true;
  539. void PrintPropSkippedReport()
  540. {
  541. ++s_frameCount;
  542. if ( s_frameCount < kFrameReportInterval )
  543. return;
  544. Msg( "%5d skipped props per frame\n", s_skippedProps / s_frameCount );
  545. s_frameCount = 0;
  546. s_skippedProps = 0;
  547. // Copy the data from s_highProps to a vector so that we can sort it by frequency.
  548. std::vector<std::pair<int, std::string>> sorted;
  549. for( auto data : s_highProps )
  550. sorted.push_back(std::pair<int, std::string>( data.second, data.first));
  551. std::sort( sorted.begin(), sorted.end() );
  552. std::reverse( sorted.begin(), sorted.end() );
  553. if ( bReportDetails )
  554. {
  555. for ( int i = 0; i < kReportLength && i < (int)sorted.size(); ++i )
  556. {
  557. Msg(" Sent %4d times, %s\n", sorted[i].first, sorted[i].second.c_str());
  558. }
  559. }
  560. s_highProps.clear();
  561. }
  562. #else
  563. void PrintPropSkippedReport()
  564. {
  565. }
  566. #endif
  567. template< bool bDebugWatch >
  568. void SendTable_WritePropList_Guts(
  569. const SendTable *pTable,
  570. SerializedEntityHandle_t toEntity,
  571. bf_write *pOut,
  572. const int objectID,
  573. CalcDeltaResultsList_t *pVecChanges // NULL == Send ALL!
  574. )
  575. {
  576. CSerializedEntity *pToEntity = (CSerializedEntity *)toEntity;
  577. if ( !pToEntity )
  578. return;
  579. CDeltaBitsWriter deltaBitsWriter( pOut );
  580. uint8 packedData[MAX_PACKEDENTITY_DATA];
  581. bf_write fieldDataBuf( "CFlattenedSerializer::WriteFieldList fieldDataBuf", packedData, sizeof( packedData ) );
  582. CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
  583. if ( !pVecChanges || pVecChanges->Count() > 0 )
  584. {
  585. CSerializedEntityFieldIterator iterator( pToEntity );
  586. iterator.First();
  587. bf_read inputFieldData;
  588. inputFieldData.SetDebugName( "CFlattenedSerializer::WriteFieldList" );
  589. pToEntity->StartReading( inputFieldData );
  590. // Ok, they want to specify a small list of properties to check.
  591. CFieldPath toField = iterator.GetField();
  592. int i = 0;
  593. while ( !pVecChanges || i < pVecChanges->Count() )
  594. {
  595. if ( pVecChanges && toField < pVecChanges->Element( i ) )
  596. {
  597. // Keep nFieldIndex, nFieldCount, fieldPaths, and nextElement as local variables
  598. // instead of members of iterator to improve code-gen. Otherwise VC++ or gcc
  599. // will refetch some of them on each iteration.
  600. int nFieldIndex = iterator.GetIndex();
  601. int nFieldCount = pToEntity->GetFieldCount();
  602. const short* fieldPaths = pToEntity->GetFieldPaths();
  603. int nextElement = pVecChanges->Element(i);
  604. while ( toField < nextElement )
  605. {
  606. #ifdef PROP_SKIPPED_REPORT
  607. ++s_skippedProps;
  608. #endif
  609. ++nFieldIndex;
  610. if ( nFieldIndex >= nFieldCount )
  611. break;
  612. toField = fieldPaths[ nFieldIndex ];
  613. }
  614. // Update the state of iterator for the new index
  615. toField = iterator.SetIndex( nFieldIndex );
  616. }
  617. if ( toField == PROP_SENTINEL )
  618. {
  619. break;
  620. }
  621. else if ( !pVecChanges ||
  622. toField == pVecChanges->Element( i ) )
  623. {
  624. // See how many bits the data for this property takes up.
  625. int nTotalBits = iterator.GetLength();
  626. Assert( nTotalBits > 0 );
  627. inputFieldData.Seek( iterator.GetOffset() );
  628. #ifdef PROP_SKIPPED_REPORT
  629. if ( toField > kMinPropOffset && pVecChanges && pVecChanges->Count() < 20 )
  630. {
  631. const SendProp *pProp = pPrecalc->GetProp( toField );
  632. char buffer[1000];
  633. V_sprintf_safe( buffer, "%s, field %d", pProp->GetName(), toField );
  634. s_highProps[ buffer ] += 1;
  635. }
  636. #endif
  637. if ( bDebugWatch )
  638. {
  639. const SendProp *pProp = pPrecalc->GetProp( toField );
  640. Assert( pProp );
  641. ShowEncodeDeltaWatchInfo( pTable, pProp, inputFieldData, objectID, toField );
  642. }
  643. fieldDataBuf.WriteBitsFromBuffer( &inputFieldData, nTotalBits );
  644. TRACE_PACKET( ( " Send Field (%s) = %d (%d bytes)\n", pPrecalc->GetProp( toField )->GetName(), nTotalBits, Bits2Bytes( nTotalBits ) ) );
  645. // Write the data into the output.
  646. deltaBitsWriter.WritePropIndex( toField );
  647. toField = iterator.Next();
  648. }
  649. ++i;
  650. }
  651. }
  652. deltaBitsWriter.Finish();
  653. // Now append the field data to the stream of paths
  654. pOut->WriteBits( packedData, fieldDataBuf.GetNumBitsWritten() );
  655. }
  656. void SendTable_WritePropList(
  657. const SendTable *pTable,
  658. SerializedEntityHandle_t toEntity,
  659. bf_write *pOut,
  660. const int objectID,
  661. CalcDeltaResultsList_t *pVecChanges // NULL == Send ALL!
  662. )
  663. {
  664. bool bDebugWatch = Sendprop_UsingDebugWatch();
  665. if ( bDebugWatch )
  666. {
  667. s_debug_info_shown = false;
  668. s_debug_bits_start = pOut->GetNumBitsWritten();
  669. SendTable_WritePropList_Guts< true >( pTable, toEntity, pOut, objectID, pVecChanges );
  670. if ( s_debug_info_shown )
  671. {
  672. int bits = pOut->GetNumBitsWritten() - s_debug_bits_start;
  673. ConDMsg( "= %i bits (%i bytes)\n", bits, Bits2Bytes(bits) );
  674. }
  675. }
  676. else
  677. {
  678. SendTable_WritePropList_Guts< false >( pTable, toEntity, pOut, objectID, pVecChanges );
  679. }
  680. }
  681. //given two data pointers and a bit range, this will determine if there is any difference between the bits. Note that this assumes that these can be interpreted as valid uint32's for speed
  682. static inline bool DoesBitRangeMatch( const uint32* RESTRICT pOld, const uint32* RESTRICT pNew, uint32 nStartBit, uint32 nEndBit )
  683. {
  684. //determine the starting index
  685. const uint32 nStartIndex = nStartBit / 32;
  686. const uint32 nEndIndex = ( nEndBit + 31 ) / 32;
  687. //determine bit overlaps
  688. const uint32 nStartIgnoreBits = nStartBit % 32;
  689. const uint32 nEndIgnoreBits = nEndIndex * 32 - nEndBit;
  690. //now run through until we find a diff
  691. const uint32* pCurrOld = pOld + nStartIndex;
  692. const uint32* pCurrNew = pNew + nStartIndex;
  693. const uint32* pEndNew = pNew + nEndIndex;
  694. for(; pCurrNew != pEndNew; ++pCurrNew, ++pCurrOld )
  695. {
  696. #if defined (_PS3) || defined (_X360)
  697. uint32 nDiff = DWordSwap((*pCurrNew ^ *pCurrOld));
  698. #else
  699. uint32 nDiff = (*pCurrNew ^ *pCurrOld);
  700. #endif
  701. if( nDiff )
  702. {
  703. //we have a diff, handle masking our range
  704. uint32 nDeltaStart = ( pCurrNew - pNew ) * 32;
  705. uint32 nDeltaEnd = nDeltaStart + 32;
  706. if( nDeltaStart < nStartBit )
  707. nDiff &= ( 0xFFFFFFFF << nStartIgnoreBits );
  708. if( nEndBit < nDeltaEnd )
  709. nDiff &= ( 0xFFFFFFFF >> nEndIgnoreBits );
  710. if( nDiff )
  711. return false;
  712. }
  713. }
  714. //no difference
  715. return true;
  716. }
  717. //given a serialized entity and a property field, this will attempt to find the property field within that entity using the fact that field = index in nearly all cases. This will account for minor adjustments. If the
  718. //serialized entities ever become sparse though, this should likely be replaced with a sorted iteration over the field, rather than this sparse lookup, but for now this is significantly faster. This will return -1 if
  719. //the field is not in the entity
  720. static inline int32 GetFieldIndex( const short* RESTRICT pFieldStart, const short* RESTRICT pFieldEnd, short nDesiredField )
  721. {
  722. //determine our starting pointer (likely to be the field itself)
  723. const short* pCurr = MIN( pFieldEnd - 1, pFieldStart + nDesiredField );
  724. //check for a direct hit (most common case)
  725. if( *pCurr == nDesiredField )
  726. return (pCurr - pFieldStart);
  727. //determine the direction that we need to head
  728. if( *pCurr < nDesiredField )
  729. {
  730. //advance forward looking for a match (or when our numbers become larger than our target)
  731. for( pCurr++; ( pCurr < pFieldEnd ) && ( *pCurr <= nDesiredField ) ; pCurr++ )
  732. {
  733. if( *pCurr == nDesiredField )
  734. return (pCurr - pFieldStart);
  735. }
  736. }
  737. else
  738. {
  739. //move backwards (or when our numbers become smaller than our target)
  740. for( pCurr--; ( pCurr >= pFieldStart ) && ( *pCurr >= nDesiredField ) ; pCurr-- )
  741. {
  742. if( *pCurr == nDesiredField )
  743. return (pCurr - pFieldStart);
  744. }
  745. }
  746. //no match, hit the end of the list
  747. return -1;
  748. }
  749. //given an entity that has had a partial change, this will take the previous data block, and splice in the new values, setting new change fields to the specified tick for ones that have actually changed. This will
  750. //return false if a delta table is not applicable and instead a full pack needs to be done
  751. SerializedEntityHandle_t SendTable_BuildDeltaProperties( edict_t *pEdict, int nObjectID, const SendTable* pTable, SerializedEntityHandle_t oldProps, CalcDeltaResultsList_t &deltaProps, CUtlMemory<CSendProxyRecipients> *pRecipients, bool& bCanReuseOldData )
  752. {
  753. const CSerializedEntity* RESTRICT pOldProps = ( const CSerializedEntity* )oldProps;
  754. //assume that we can't reuse the old frame for now
  755. bCanReuseOldData = false;
  756. // Setup all the info we'll be walking the tree with.
  757. CServerDatatableStack info( pTable->m_pPrecalc, (unsigned char*)pEdict->GetUnknown(), nObjectID );
  758. info.m_pRecipients = pRecipients; // optional buffer to store the bits for which clients get what data.
  759. info.Init( false, false );
  760. //get the change list associated with this object
  761. const CEdictChangeInfo * RESTRICT pCI = &g_pSharedChangeInfo->m_ChangeInfos[ pEdict->GetChangeInfo() ];
  762. //cache our indices for our fields
  763. const uint32 nOldProps = pOldProps->GetFieldCount();
  764. const short* RESTRICT pFieldListStart = pOldProps->GetFieldPaths();
  765. const short* RESTRICT pFieldListEnd = pFieldListStart + nOldProps;
  766. //now copy over the old property block into our new buffer
  767. uint8 packedData[MAX_PACKEDENTITY_DATA];
  768. memcpy( packedData, pOldProps->GetFieldData(), Bits2Bytes( pOldProps->GetFieldDataBitCount() ) );
  769. bf_write fieldDataBuf( "BuildDeltaProperties fieldDataBuf", packedData, sizeof( packedData ) );
  770. //we now need to build a list of the properties that have changed, converting them over to indices, and sorting them
  771. for( uint32 nCurrOffset = 0; nCurrOffset < (uint32)pCI->m_nChangeOffsets; ++nCurrOffset )
  772. {
  773. int propOffsets[MAX_PROP_INDEX_OFFSETS];
  774. int numPropsMatchingIndex = pTable->m_pPrecalc->GetPropertyIndexFromOffset( pCI->m_ChangeOffsets[ nCurrOffset ], propOffsets );
  775. if ( !numPropsMatchingIndex )
  776. {
  777. continue;
  778. }
  779. for ( int propOffsetIndex = 0; propOffsetIndex < numPropsMatchingIndex; ++propOffsetIndex )
  780. {
  781. int nPropField = propOffsets[propOffsetIndex];
  782. //we can safely skip any properties that are not considered valid by the proxy
  783. if( !info.IsPropProxyValid( nPropField ) )
  784. continue;
  785. //we now need to map this property field to an index in our serialized entity
  786. int nFieldIndex = GetFieldIndex( pFieldListStart, pFieldListEnd, ( short )nPropField );
  787. //if we didn't find a match here, we have a problem of mismatch between the blocks and we need to fall back to the slow route
  788. if( nFieldIndex == -1 )
  789. return SERIALIZED_ENTITY_HANDLE_INVALID;
  790. //we have a match, so we need to splice this new property onto our original property
  791. const uint32 nPropBitStart = pOldProps->GetFieldDataBitOffset( nFieldIndex );
  792. const uint32 nPropBitEnd = pOldProps->GetFieldDataBitEndOffset( nFieldIndex );
  793. fieldDataBuf.SeekToBit( nPropBitStart );
  794. info.SeekToProp( nPropField );
  795. const SendProp* RESTRICT pProp = pTable->m_pPrecalc->GetProp( nPropField );
  796. // Call their proxy to get the property's value.
  797. DVariant var;
  798. pProp->GetProxyFn()(
  799. pProp, info.GetCurStructBase(), info.GetCurStructBase() + pProp->GetOffset(),
  800. &var, 0, info.GetObjectID() );
  801. //now write it out
  802. g_PropTypeFns[pProp->m_Type].Encode(
  803. info.GetCurStructBase(), &var, pProp,
  804. &fieldDataBuf, info.GetObjectID() );
  805. //handle if the property changed in size (does this happen? If so, we just need to fall back to the slow, slow route, and should return NULL)
  806. if( nPropBitEnd != ( uint32 )fieldDataBuf.GetNumBitsWritten() )
  807. return SERIALIZED_ENTITY_HANDLE_INVALID;
  808. //we now need to compare if we actually changed values between these bits
  809. if( !DoesBitRangeMatch( ( const uint32* )pOldProps->GetFieldData(), ( const uint32* )packedData, nPropBitStart, nPropBitEnd ) )
  810. deltaProps.AddToTail( nPropField );
  811. }
  812. }
  813. //if we have no changes, we can just reuse the old one, don't bother making a copy
  814. if( deltaProps.Count() == 0 )
  815. {
  816. bCanReuseOldData = true;
  817. return SERIALIZED_ENTITY_HANDLE_INVALID;
  818. }
  819. //at this point, we have all of our new data. So setup our new serialized version to return
  820. CSerializedEntity* RESTRICT pNewProps = (CSerializedEntity*)g_pSerializedEntities->AllocateSerializedEntity(__FILE__, __LINE__);
  821. pNewProps->SetupPackMemory( nOldProps, pOldProps->GetFieldDataBitCount() );
  822. //and blit on over
  823. memcpy( pNewProps->GetFieldPaths(), pOldProps->GetFieldPaths(), sizeof( uint16 ) * nOldProps );
  824. memcpy( pNewProps->GetFieldDataBitOffsets(), pOldProps->GetFieldDataBitOffsets(), sizeof( uint32 ) * nOldProps );
  825. memcpy( pNewProps->GetFieldData(), packedData, Bits2Bytes( pNewProps->GetFieldDataBitCount() ) );
  826. return ( SerializedEntityHandle_t )pNewProps;
  827. }
  828. static void BuildNonZeroDelta( const SendTable *pTable,
  829. const CSerializedEntity *pToEntity,
  830. const int objectID,
  831. CalcDeltaResultsList_t &deltaProps )
  832. {
  833. //setup a reader for this data
  834. bf_read ToBits;
  835. pToEntity->StartReading( ToBits );
  836. //just run through each source property, and for each one that is non-zero, add it to the delta props
  837. const uint32 nNumProps = (uint32)pToEntity->GetFieldCount();
  838. for( uint32 nCurrProp = 0; nCurrProp < nNumProps; ++nCurrProp )
  839. {
  840. CFieldPath nPath = pToEntity->GetFieldPath( nCurrProp );
  841. ToBits.Seek(pToEntity->GetFieldDataBitOffset( nCurrProp ) );
  842. const SendProp *pProp = pTable->m_pPrecalc->GetProp( nPath );
  843. if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &ToBits ) )
  844. {
  845. // add non-zero property to index list
  846. deltaProps.AddToTail( nPath );
  847. }
  848. }
  849. }
  850. //given data, this will continue to advance the pointers so long as from = to, and will update the necessary values
  851. static FORCEINLINE void AdvanceToNextChange( const uint32* RESTRICT pFrom, const uint32* RESTRICT pTo, uint32 nMaxInts, uint32* RESTRICT pnIntOffset, uint32* RESTRICT pnDataDiff, uint32* RESTRICT pnDataStartBit )
  852. {
  853. const uint32* RESTRICT pEndFrom = pFrom + nMaxInts;
  854. const uint32* RESTRICT pCurrFrom = pFrom + *pnIntOffset;
  855. const uint32* RESTRICT pCurrTo = pTo + *pnIntOffset;
  856. for( ; pCurrFrom != pEndFrom; ++pCurrFrom, ++pCurrTo )
  857. {
  858. if( *pCurrFrom != *pCurrTo )
  859. {
  860. #if defined(_PS3) || defined(_X360)
  861. *pnDataDiff = DWordSwap((*pCurrFrom ^ *pCurrTo));
  862. #else
  863. *pnDataDiff = *pCurrFrom ^ *pCurrTo;
  864. #endif
  865. break;
  866. }
  867. }
  868. *pnIntOffset = ( pCurrFrom - pFrom );
  869. *pnDataStartBit = ( pCurrFrom - pFrom ) * 32;
  870. }
  871. //given a starting bit index, the dirty bit window, and the range that the property covers, this will determine if this property overlaps the dirty bit
  872. static FORCEINLINE bool DoesPropertyOverlapDirtyBit( uint32 nDataStartBit, uint32 nFieldStartBit, uint32 nFieldEndBit, uint32 nDataDiff )
  873. {
  874. //Note, we assume that any properties that share this integer with us have been tested prior, and as a result, also cleared out the data diff
  875. //when they checked so we do not need to mask off the lower bound
  876. Assert( nFieldEndBit > nDataStartBit );
  877. Assert( nDataStartBit + 32 > nFieldStartBit );
  878. //see if this integer spans the boundary where we end, if so, we need to only check the area that we cover
  879. uint32 nDataEndBit = nDataStartBit + 32;
  880. if( nFieldEndBit < nDataEndBit )
  881. {
  882. uint32 nEndBits = ( nDataEndBit - nFieldEndBit );
  883. Assert( nEndBits < 32 );
  884. uint32 nEndMask = ( 0xFFFFFFFF >> nEndBits );
  885. nDataDiff &= nEndMask;
  886. }
  887. return ( nDataDiff != 0 );
  888. }
  889. //this is a fast path that will handle scanning through the properties, identifying differences in the values. This will build a list of properties that
  890. //have differing values, and if it encounters a misalignment between the two property lists (like a property added, removed, or changed in size) it will
  891. //stop processing and return the property index it had to stop on so that the slower unaligned path can be used from that point on
  892. static inline uint32 FastPathCalcDelta( const CSerializedEntity *pFromEntity, const CSerializedEntity *pToEntity, CalcDeltaResultsList_t &deltaProps )
  893. {
  894. //cache common data
  895. const uint32 nFromFieldCount = pFromEntity->GetFieldCount();
  896. const uint32 nToFieldCount = pToEntity->GetFieldCount();
  897. const uint32* RESTRICT pFromData = (const uint32*)pFromEntity->GetFieldData();
  898. const uint32* RESTRICT pToData = (const uint32*)pToEntity->GetFieldData();
  899. //determine how many integers we have in our buffer
  900. const uint32 nEndDataInts = ( MIN( pToEntity->GetFieldDataBitCount(), pFromEntity->GetFieldDataBitCount() ) + 31 ) / 32;
  901. //scan through our data to find the first area where we detect a change
  902. uint32 nIntOffset = 0;
  903. uint32 nDataDiff = 0;
  904. uint32 nDataStartBit = 0;
  905. AdvanceToNextChange( pFromData, pToData, nEndDataInts, &nIntOffset, &nDataDiff, &nDataStartBit );
  906. //handle a special case where all the data was the same
  907. if( ( nIntOffset == nEndDataInts ) && ( nFromFieldCount == nToFieldCount ) && ( pToEntity->GetFieldDataBitCount() == pFromEntity->GetFieldDataBitCount() ) )
  908. {
  909. //odds are very good that we have the same property layout, so just do an entire comparison of the values
  910. if( ( V_memcmp( pFromEntity->GetFieldDataBitOffsets(), pToEntity->GetFieldDataBitOffsets(), nFromFieldCount * sizeof( uint32 ) ) == 0 ) &&
  911. ( V_memcmp( pFromEntity->GetFieldPaths(), pToEntity->GetFieldPaths(), nFromFieldCount * sizeof( short ) ) == 0 ) )
  912. {
  913. return nFromFieldCount;
  914. }
  915. //the field layout wasn't the same. VERY rare, fall through and let the function below catch where
  916. }
  917. //determine how many overlapping fields we can test against
  918. uint32 nMinFieldOverlap = MIN( nFromFieldCount, nToFieldCount );
  919. if( nMinFieldOverlap == 0 )
  920. return 0;
  921. //pointers to each of the data blocks we are reading from
  922. const short* RESTRICT pFromPath = pFromEntity->GetFieldPaths();
  923. const short* RESTRICT pToPath = pToEntity->GetFieldPaths();
  924. //since we want the END of the properties for most operations
  925. const uint32* RESTRICT pFromStartOffset = pFromEntity->GetFieldDataBitOffsets();
  926. const uint32* RESTRICT pFromEndOffset = pFromEntity->GetFieldDataBitOffsets() + 1;
  927. const uint32* RESTRICT pToEndOffset = pToEntity->GetFieldDataBitOffsets() + 1;
  928. //run through all the properties...except the last one. The reason for this is because we don't want to have to range check on all of our property bit offsets
  929. uint32 nLastField = nMinFieldOverlap - 1;
  930. for(uint32 nCurrField = 0 ; nCurrField < nLastField; ++nCurrField )
  931. {
  932. //extract information about our fields to make sure that they match
  933. const CFieldPath nFromPath = pFromPath[ nCurrField ];
  934. const CFieldPath nToPath = pToPath[ nCurrField ];
  935. const uint32 nFromEnd = pFromEndOffset[ nCurrField ];
  936. const uint32 nToEnd = pToEndOffset[ nCurrField ];
  937. //we can do the fast path so long as the field indices match (no dropped properties) and the offsets are the same (which creates unaligned properties)
  938. if( ( nFromPath != nToPath ) || ( nFromEnd != nToEnd ) )
  939. return nCurrField;
  940. //see if we have completely clear data already (we have clear data already past where we end)
  941. if( nDataStartBit >= nFromEnd )
  942. continue;
  943. //we can now tell if we have a dirty prop
  944. if( DoesPropertyOverlapDirtyBit( nDataStartBit, pFromStartOffset[ nCurrField ], nFromEnd, nDataDiff ) )
  945. deltaProps.AddToTail( nFromPath );
  946. //and handle reading to the next diff which is either at the end of our property, or the next integer if we have no more dirty bits in this one
  947. uint32 nPropEndInt = nFromEnd / 32;
  948. if( nPropEndInt > nIntOffset )
  949. {
  950. //we have advanced past our previous buffer, so scan to our next change
  951. nIntOffset = nPropEndInt;
  952. AdvanceToNextChange( pFromData, pToData, nEndDataInts, &nIntOffset, &nDataDiff, &nDataStartBit );
  953. }
  954. //the property may have dirty data in its last section, so see if we overlap, if so, clear our portion, and see if we need to continue the scan
  955. if( nIntOffset == nPropEndInt )
  956. {
  957. //clear any portion of this integer that we overlap with
  958. uint32 nOverlapBits = nFromEnd - nDataStartBit;
  959. Assert( nOverlapBits < 32 );
  960. uint32 nKeepMask = 0xFFFFFFFFF << nOverlapBits;
  961. nDataDiff &= nKeepMask;
  962. //and if we have no more dirty data in the integer, we need to scan to the next change
  963. if( nDataDiff == 0 )
  964. {
  965. nIntOffset++;
  966. AdvanceToNextChange( pFromData, pToData, nEndDataInts, &nIntOffset, &nDataDiff, &nDataStartBit );
  967. }
  968. }
  969. }
  970. //and test our last one
  971. {
  972. //extract information about our fields to make sure that they match
  973. const CFieldPath nFromPath = pFromPath[ nLastField ];
  974. const CFieldPath nToPath = pToPath[ nLastField ];
  975. const uint32 nFromEnd = pFromEntity->GetFieldDataBitCount();
  976. const uint32 nToEnd = pToEntity->GetFieldDataBitCount();
  977. //we can do the fast path so long as the field indices match (no dropped properties) and the offsets are the same (which creates unaligned properties)
  978. if( ( nFromPath != nToPath ) || ( nFromEnd != nToEnd ) )
  979. return nLastField;
  980. //see if we have completely clear data already (we have clear data already past where we end)
  981. if( nDataStartBit < nFromEnd )
  982. {
  983. //we can now tell if we have a dirty prop
  984. if( DoesPropertyOverlapDirtyBit( nDataStartBit, pFromStartOffset[ nLastField ], nFromEnd, nDataDiff ) )
  985. deltaProps.AddToTail( nFromPath );
  986. }
  987. }
  988. //let the caller know how many properties we took care of
  989. return nMinFieldOverlap;
  990. }
  991. //this will handle further delta detection on the properties using a much slower approach to handle misalignments
  992. static void SlowPathCompareProps( const CSerializedEntity *pFromEntity, const CSerializedEntity *pToEntity, uint32 nStartField, CalcDeltaResultsList_t &deltaProps )
  993. {
  994. //run through each of our target properties that we have left
  995. const uint32 nNumToFields = ( uint32 )pToEntity->GetFieldCount();
  996. const uint32 nNumFromFields = ( uint32 )pFromEntity->GetFieldCount();
  997. uint32 nCurrFromField = nStartField;
  998. bf_read ToData, FromData;
  999. pToEntity->StartReading( ToData );
  1000. pFromEntity->StartReading( FromData );
  1001. for( uint32 nCurrToField = nStartField; nCurrToField < nNumToFields; ++nCurrToField )
  1002. {
  1003. //get our path for our target property
  1004. const CFieldPath nToPath = pToEntity->GetFieldPath( nCurrToField );
  1005. //find a possible match in our from path
  1006. for( ; ( nCurrFromField < nNumFromFields ) && ( pFromEntity->GetFieldPath( nCurrFromField ) < nToPath ) ; nCurrFromField++ )
  1007. {
  1008. }
  1009. //handle the case where this is a new property
  1010. if( ( nCurrFromField < nNumFromFields ) && ( pFromEntity->GetFieldPath( nCurrFromField ) == nToPath ) )
  1011. {
  1012. //position our buffers to the start of each property
  1013. uint32 nToDataStartBit = pToEntity->GetFieldDataBitOffset( nCurrToField );
  1014. uint32 nFromDataStartBit = pFromEntity->GetFieldDataBitOffset( nCurrFromField );
  1015. uint32 nToDataSize = pToEntity->GetFieldDataBitEndOffset( nCurrToField ) - nToDataStartBit;
  1016. uint32 nFromDataSize = pFromEntity->GetFieldDataBitEndOffset( nCurrFromField ) - nFromDataStartBit;
  1017. ToData.Seek( nToDataStartBit );
  1018. FromData.Seek( nFromDataStartBit );
  1019. if( ( nToDataSize != nFromDataSize ) || !CompareBits( FromData, ToData, nToDataSize ) )
  1020. {
  1021. deltaProps.AddToTail( nToPath );
  1022. }
  1023. }
  1024. else
  1025. {
  1026. //this is a new property that we need to encode
  1027. deltaProps.AddToTail( nToPath );
  1028. }
  1029. }
  1030. }
  1031. void SendTable_CalcDelta(
  1032. const SendTable *pTable,
  1033. SerializedEntityHandle_t fromEntity, // can be null
  1034. SerializedEntityHandle_t toEntity,
  1035. const int objectID,
  1036. CalcDeltaResultsList_t &deltaProps )
  1037. {
  1038. const CSerializedEntity *pFromEntity = (CSerializedEntity *)fromEntity;
  1039. const CSerializedEntity *pToEntity = (CSerializedEntity *)toEntity;
  1040. //if we don't have a from entity, the problem domain is to just go through the current entity and build a change list of which properties are non-zero
  1041. if( pFromEntity == NULL )
  1042. {
  1043. BuildNonZeroDelta( pTable, pToEntity, objectID, deltaProps );
  1044. return;
  1045. }
  1046. //fast path compare as many properties as we can, this handles the vast majority of properties which only have value changes
  1047. uint32 nFastPathProps = FastPathCalcDelta( pFromEntity, pToEntity, deltaProps );
  1048. //see if we have any properties not yet checked, meaning we had differences in the property layout
  1049. if( nFastPathProps < ( uint32 )pToEntity->GetFieldCount() )
  1050. {
  1051. //now we need to fall back to the slow path for the remaining un-aligned data, this will either have zero iterations (made it all the way through with fast properties),
  1052. //or will cover all the properties after a divergence
  1053. SlowPathCompareProps( pFromEntity, pToEntity, nFastPathProps, deltaProps );
  1054. }
  1055. }
  1056. bool SendTable_WriteInfos( const SendTable *pTable, bf_write& bfWrite, bool bNeedsDecoder, bool bIsEnd )
  1057. {
  1058. CSVCMsg_SendTable_t msg;
  1059. if( !pTable || bIsEnd )
  1060. {
  1061. // Write the end bit to signal no more send tables
  1062. Assert( !pTable && bIsEnd );
  1063. msg.set_is_end( true );
  1064. return msg.WriteToBuffer( bfWrite );
  1065. }
  1066. if ( bNeedsDecoder ) // only set if true, let the default false
  1067. {
  1068. msg.set_needs_decoder( bNeedsDecoder );
  1069. }
  1070. msg.set_net_table_name( pTable->GetName() );
  1071. // Send each property.
  1072. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  1073. {
  1074. const SendProp *pProp = &pTable->m_pProps[iProp];
  1075. CSVCMsg_SendTable::sendprop_t *pSendProp = msg.add_props();
  1076. pSendProp->set_type( pProp->m_Type );
  1077. pSendProp->set_var_name( pProp->GetName() );
  1078. // we now have some flags that aren't networked so strip them off
  1079. unsigned int networkFlags = pProp->GetFlags() & ((1<<SPROP_NUMFLAGBITS_NETWORKED)-1);
  1080. pSendProp->set_flags( networkFlags );
  1081. pSendProp->set_priority( pProp->GetPriority() );
  1082. if( pProp->m_Type == DPT_DataTable )
  1083. {
  1084. // Just write the name and it will be able to reuse the table with a matching name.
  1085. pSendProp->set_dt_name( pProp->GetDataTable()->m_pNetTableName );
  1086. }
  1087. else if ( pProp->IsExcludeProp() )
  1088. {
  1089. pSendProp->set_dt_name( pProp->GetExcludeDTName() );
  1090. }
  1091. else if ( pProp->GetType() == DPT_Array )
  1092. {
  1093. pSendProp->set_num_elements( pProp->GetNumElements() );
  1094. }
  1095. else
  1096. {
  1097. pSendProp->set_low_value( pProp->m_fLowValue );
  1098. pSendProp->set_high_value( pProp->m_fHighValue );
  1099. pSendProp->set_num_bits( pProp->m_nBits );
  1100. }
  1101. }
  1102. return msg.WriteToBuffer( bfWrite );
  1103. }
  1104. // Spits out warnings for invalid properties and forces property values to
  1105. // be in valid ranges for the encoders and decoders.
  1106. static void SendTable_Validate( CSendTablePrecalc *pPrecalc )
  1107. {
  1108. SendTable *pTable = pPrecalc->m_pSendTable;
  1109. for( int i=0; i < pTable->m_nProps; i++ )
  1110. {
  1111. SendProp *pProp = &pTable->m_pProps[i];
  1112. if ( pProp->GetArrayProp() )
  1113. {
  1114. if ( pProp->GetArrayProp()->GetType() == DPT_DataTable )
  1115. {
  1116. 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() );
  1117. }
  1118. }
  1119. else
  1120. {
  1121. ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) );
  1122. }
  1123. // Check for 1-bit signed properties (their value doesn't get down to the client).
  1124. if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) )
  1125. {
  1126. 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());
  1127. }
  1128. }
  1129. for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
  1130. {
  1131. const SendProp *pProp = pPrecalc->GetProp( i );
  1132. if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
  1133. {
  1134. pTable->SetHasPropsEncodedAgainstTickcount( true );
  1135. break;
  1136. }
  1137. }
  1138. }
  1139. static void SendTable_CalcNextVectorElems( SendTable *pTable )
  1140. {
  1141. for ( int i=0; i < pTable->GetNumProps(); i++ )
  1142. {
  1143. SendProp *pProp = pTable->GetProp( i );
  1144. if ( pProp->GetType() == DPT_DataTable )
  1145. {
  1146. SendTable_CalcNextVectorElems( pProp->GetDataTable() );
  1147. }
  1148. else if ( pProp->GetOffset() < 0 )
  1149. {
  1150. pProp->SetOffset( -pProp->GetOffset() );
  1151. pProp->SetFlags( pProp->GetFlags() | SPROP_IS_A_VECTOR_ELEM );
  1152. }
  1153. }
  1154. }
  1155. // This helps us figure out which properties can use the super-optimized mode
  1156. // where they are tracked in a list when they change. If their m_pProxies pointers
  1157. // are set to 1, then it means that this property is gotten to by means of SendProxy_DataTableToDataTable.
  1158. // If it's set to 0, then we can't directly take the property's offset.
  1159. class CPropOffsetStack : public CDatatableStack
  1160. {
  1161. public:
  1162. CPropOffsetStack( CSendTablePrecalc *pPrecalc ) :
  1163. CDatatableStack( pPrecalc, NULL, -1 )
  1164. {
  1165. m_pPropMapStackPrecalc = pPrecalc;
  1166. }
  1167. inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
  1168. {
  1169. const SendProp *pProp = m_pPropMapStackPrecalc->GetDatatableProp( iProp );
  1170. return pStructBase + pProp->GetOffset();
  1171. }
  1172. virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
  1173. {
  1174. // Remember where the game code pointed us for this datatable's data so
  1175. m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
  1176. for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
  1177. {
  1178. CSendNode *pCurChild = pNode->GetChild( iChild );
  1179. unsigned char *pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
  1180. RecurseAndCallProxies( pCurChild, pNewStructBase );
  1181. }
  1182. }
  1183. public:
  1184. CSendTablePrecalc *m_pPropMapStackPrecalc;
  1185. };
  1186. static void BuildPropOffsetToIndexMap( CSendTablePrecalc *pPrecalc )
  1187. {
  1188. CPropOffsetStack pmStack( pPrecalc );
  1189. pmStack.Init( false, false );
  1190. const int nNumProps = pPrecalc->m_Props.Count();
  1191. pPrecalc->m_PropOffsetToIndex.SetSize( nNumProps );
  1192. for ( int i=0; i < nNumProps; i++ )
  1193. {
  1194. pmStack.SeekToProp( i );
  1195. const SendProp *pProp = pPrecalc->m_Props[i];
  1196. int offset = size_cast<int>( pProp->GetOffset() + (intp)pmStack.GetCurStructBase() );
  1197. pPrecalc->m_PropOffsetToIndex[ i ].m_nIndex = i;
  1198. pPrecalc->m_PropOffsetToIndex[ i ].m_nOffset = offset;
  1199. }
  1200. //now sort our list to ensure it is efficient for lookup
  1201. std::sort( pPrecalc->m_PropOffsetToIndex.Base(), pPrecalc->m_PropOffsetToIndex.Base() + pPrecalc->m_PropOffsetToIndex.Count() );
  1202. }
  1203. static bool SendTable_InitTable( SendTable *pTable )
  1204. {
  1205. if( pTable->m_pPrecalc )
  1206. return true;
  1207. // Create the CSendTablePrecalc.
  1208. CSendTablePrecalc *pPrecalc = new CSendTablePrecalc;
  1209. pTable->m_pPrecalc = pPrecalc;
  1210. pPrecalc->m_pSendTable = pTable;
  1211. pTable->m_pPrecalc = pPrecalc;
  1212. SendTable_CalcNextVectorElems( pTable );
  1213. // Bind the instrumentation if -dti was specified.
  1214. pPrecalc->m_pDTITable = ServerDTI_HookTable( pTable );
  1215. // Setup its flat property array.
  1216. if ( !pPrecalc->SetupFlatPropertyArray() )
  1217. return false;
  1218. BuildPropOffsetToIndexMap( pPrecalc );
  1219. SendTable_Validate( pPrecalc );
  1220. return true;
  1221. }
  1222. static void SendTable_TermTable( SendTable *pTable )
  1223. {
  1224. if( !pTable->m_pPrecalc )
  1225. return;
  1226. delete pTable->m_pPrecalc;
  1227. Assert( !pTable->m_pPrecalc ); // Make sure it unbound itself.
  1228. }
  1229. int SendTable_GetNumFlatProps( const SendTable *pSendTable )
  1230. {
  1231. const CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  1232. ErrorIfNot( pPrecalc,
  1233. ("SendTable_GetNumFlatProps: missing pPrecalc.")
  1234. );
  1235. return pPrecalc->GetNumProps();
  1236. }
  1237. CRC32_t SendTable_CRCTable( CRC32_t &crc, SendTable *pTable )
  1238. {
  1239. CRC32_ProcessBuffer( &crc, (void *)pTable->m_pNetTableName, Q_strlen( pTable->m_pNetTableName) );
  1240. CRCMSG( "Table: %s\n", pTable->m_pNetTableName );
  1241. int nProps = LittleLong( pTable->m_nProps );
  1242. CRC32_ProcessBuffer( &crc, (void *)&nProps, sizeof( pTable->m_nProps ) );
  1243. CRCMSG( " nProps: " CRCFMT4 "\n", CRCMEM4( nProps ) );
  1244. // Send each property.
  1245. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  1246. {
  1247. const SendProp *pProp = &pTable->m_pProps[iProp];
  1248. int type = LittleLong( pProp->m_Type );
  1249. CRC32_ProcessBuffer( &crc, (void *)&type, sizeof( type ) );
  1250. CRC32_ProcessBuffer( &crc, (void *)pProp->GetName() , Q_strlen( pProp->GetName() ) );
  1251. int flags = LittleLong( pProp->GetFlags() );
  1252. CRC32_ProcessBuffer( &crc, (void *)&flags, sizeof( flags ) );
  1253. CRCMSG( " %d. %s : type = " CRCFMT4 ", flags = " CRCFMT4 ", bits = %d\n",
  1254. iProp, pProp->GetName(),
  1255. CRCMEM4( type ), CRCMEM4( flags ), pProp->m_nBits );
  1256. if( pProp->m_Type == DPT_DataTable )
  1257. {
  1258. CRC32_ProcessBuffer( &crc, (void *)pProp->GetDataTable()->m_pNetTableName, Q_strlen( pProp->GetDataTable()->m_pNetTableName ) );
  1259. CRCMSG( " DPT_DataTable: %s\n", pProp->GetDataTable()->m_pNetTableName );
  1260. if ( SendProp *pArrayPropInside = pProp->GetArrayProp() )
  1261. {
  1262. int arPropType = pArrayPropInside->m_Type;
  1263. int arPropFlags = pArrayPropInside->GetFlags();
  1264. int arElementsCount = pProp->GetDataTable()->m_nProps;
  1265. CRC32_ProcessBuffer( &crc, (void *)&arPropType, sizeof( arPropType ) );
  1266. CRC32_ProcessBuffer( &crc, (void *)&arPropFlags, sizeof( arPropFlags ) );
  1267. CRCMSG( " ArrayPropData: type = " CRCFMT4 ", flags = " CRCFMT4 ", bits = %d, size = %d\n",
  1268. CRCMEM4( arPropType ), CRCMEM4( arPropFlags ), pArrayPropInside->m_nBits, arElementsCount );
  1269. }
  1270. #if CRC_SEND_TABLE_VERBOSE
  1271. else if ( StringHasPrefixCaseSensitive( pProp->GetDataTable()->m_pNetTableName, "_ST_" ) )
  1272. {
  1273. SendProp *pArrayPropInside = pProp->GetDataTable()->GetProp( 1 );
  1274. int arPropType = pArrayPropInside->m_Type;
  1275. int arPropFlags = pArrayPropInside->GetFlags();
  1276. int arElementsCount = pProp->GetDataTable()->m_nProps - 1; // first is length proxy
  1277. CRC32_ProcessBuffer( &crc, ( void * ) &arPropType, sizeof( arPropType ) );
  1278. CRC32_ProcessBuffer( &crc, ( void * ) &arPropFlags, sizeof( arPropFlags ) );
  1279. CRCMSG( " UtlVectorData: type = " CRCFMT4 ", flags = " CRCFMT4 ", bits = %d, size = %d\n",
  1280. CRCMEM4( arPropType ), CRCMEM4( arPropFlags ), pArrayPropInside->m_nBits, arElementsCount );
  1281. }
  1282. else
  1283. {
  1284. SendTable *pSendTableReferenced = pProp->GetDataTable();
  1285. if ( ( g_SendTables.Find( pSendTableReferenced ) == g_SendTables.InvalidIndex() )
  1286. && ( g_SendTablesReferenced.Find( pSendTableReferenced ) == g_SendTablesReferenced.InvalidIndex() ) )
  1287. g_SendTablesReferenced.AddToTail( pSendTableReferenced );
  1288. }
  1289. #endif
  1290. }
  1291. else
  1292. {
  1293. if ( pProp->IsExcludeProp() )
  1294. {
  1295. CRC32_ProcessBuffer( &crc, (void *)pProp->GetExcludeDTName(), Q_strlen( pProp->GetExcludeDTName() ) );
  1296. CRCMSG( " ExcludeProp: %s\n", pProp->GetExcludeDTName() );
  1297. }
  1298. else if ( pProp->GetType() == DPT_Array )
  1299. {
  1300. int numelements = LittleLong( pProp->GetNumElements() );
  1301. CRC32_ProcessBuffer( &crc, (void *)&numelements, sizeof( numelements ) );
  1302. CRCMSG( " DPT_Array: " CRCFMT4 "\n", CRCMEM4( numelements ) );
  1303. }
  1304. else
  1305. {
  1306. float lowvalue;
  1307. LittleFloat( &lowvalue, &pProp->m_fLowValue );
  1308. CRC32_ProcessBuffer( &crc, (void *)&lowvalue, sizeof( lowvalue ) );
  1309. float highvalue;
  1310. LittleFloat( &highvalue, &pProp->m_fHighValue );
  1311. CRC32_ProcessBuffer( &crc, (void *)&highvalue, sizeof( highvalue ) );
  1312. int bits = LittleLong( pProp->m_nBits );
  1313. CRC32_ProcessBuffer( &crc, (void *)&bits, sizeof( bits ) );
  1314. CRCMSG( " DPT: lv " CRCFMT4 ", hv " CRCFMT4 ", bits " CRCFMT4 "\n",
  1315. CRCMEM4( lowvalue ), CRCMEM4( highvalue ), CRCMEM4( bits ) );
  1316. }
  1317. }
  1318. }
  1319. return crc;
  1320. }
  1321. void SendTable_PrintStats( void )
  1322. {
  1323. int numTables = 0;
  1324. int numFloats = 0;
  1325. int numStrings = 0;
  1326. int numArrays = 0;
  1327. int numInts = 0;
  1328. int numVecs = 0;
  1329. int numVecXYs = 0;
  1330. int numSubTables = 0;
  1331. int numSendProps = 0;
  1332. int numFlatProps = 0;
  1333. int numExcludeProps = 0;
  1334. for ( int i=0; i < g_SendTables.Count(); i++ )
  1335. {
  1336. SendTable *st = g_SendTables[i];
  1337. numTables++;
  1338. numSendProps += st->GetNumProps();
  1339. numFlatProps += st->m_pPrecalc->GetNumProps();
  1340. for ( int j=0; j < st->GetNumProps(); j++ )
  1341. {
  1342. SendProp* sp = st->GetProp( j );
  1343. if ( sp->IsExcludeProp() )
  1344. {
  1345. numExcludeProps++;
  1346. continue; // no real sendprops
  1347. }
  1348. if ( sp->IsInsideArray() )
  1349. continue;
  1350. switch( sp->GetType() )
  1351. {
  1352. case DPT_Int : numInts++; break;
  1353. case DPT_Float : numFloats++; break;
  1354. case DPT_Vector : numVecs++; break;
  1355. case DPT_VectorXY : numVecXYs++; break;
  1356. case DPT_String : numStrings++; break;
  1357. case DPT_Array : numArrays++; break;
  1358. case DPT_DataTable : numSubTables++; break;
  1359. }
  1360. }
  1361. }
  1362. Msg("Total Send Table stats\n");
  1363. Msg("Send Tables : %i\n", numTables );
  1364. Msg("Send Props : %i\n", numSendProps );
  1365. Msg("Flat Props : %i\n", numFlatProps );
  1366. Msg("Int Props : %i\n", numInts );
  1367. Msg("Float Props : %i\n", numFloats );
  1368. Msg("Vector Props : %i\n", numVecs );
  1369. Msg("VectorXY Props: %i\n", numVecXYs );
  1370. Msg("String Props : %i\n", numStrings );
  1371. Msg("Array Props : %i\n", numArrays );
  1372. Msg("Table Props : %i\n", numSubTables );
  1373. Msg("Exclu Props : %i\n", numExcludeProps );
  1374. }
  1375. bool SendTable_Init( SendTable **pTables, int nTables )
  1376. {
  1377. ErrorIfNot( g_SendTables.Count() == 0,
  1378. ("SendTable_Init: called twice.")
  1379. );
  1380. bool bFullSendTableInfo = false;
  1381. if ( !IsRetail() )
  1382. {
  1383. bFullSendTableInfo = ( CommandLine()->FindParm("-dtix" ) != 0 );
  1384. }
  1385. // Initialize them all.
  1386. for ( int i=0; i < nTables; i++ )
  1387. {
  1388. if ( !SendTable_InitTable( pTables[i] ) )
  1389. return false;
  1390. if ( bFullSendTableInfo && pTables[i] )
  1391. Msg( "SendTable[%03d] = %s\n", i, pTables[i]->GetName() );
  1392. }
  1393. // Store off the SendTable list.
  1394. g_SendTables.CopyArray( pTables, nTables );
  1395. g_SendTableCRC = SendTable_ComputeCRC( );
  1396. if ( CommandLine()->FindParm("-dti" ) )
  1397. {
  1398. SendTable_PrintStats();
  1399. }
  1400. return true;
  1401. }
  1402. void SendTable_Term()
  1403. {
  1404. // Term all the SendTables.
  1405. for ( int i=0; i < g_SendTables.Count(); i++ )
  1406. SendTable_TermTable( g_SendTables[i] );
  1407. // Clear the list of SendTables.
  1408. g_SendTables.Purge();
  1409. g_SendTableCRC = 0;
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Purpose: Computes the crc for all sendtables for the data sent in the class/table definitions
  1413. // Output : CRC32_t
  1414. //-----------------------------------------------------------------------------
  1415. CRC32_t SendTable_ComputeCRC()
  1416. {
  1417. CRCMSG( "-----------------------------------------------------\n" );
  1418. CRCMSG( "----------------SendTable_ComputeCRC-----------------\n" );
  1419. CRCMSG( "-----------------------------------------------------\n" );
  1420. CRC32_t result;
  1421. CRC32_Init( &result );
  1422. // walk the tables and checksum them
  1423. int c = g_SendTables.Count();
  1424. for ( int i = 0 ; i < c; i++ )
  1425. {
  1426. SendTable *st = g_SendTables[ i ];
  1427. result = SendTable_CRCTable( result, st );
  1428. }
  1429. #if CRC_SEND_TABLE_VERBOSE
  1430. for ( int i = 0; i < g_SendTablesReferenced.Count(); i++ )
  1431. {
  1432. SendTable *st = g_SendTablesReferenced[ i ];
  1433. result = SendTable_CRCTable( result, st );
  1434. }
  1435. #endif
  1436. CRC32_Final( &result );
  1437. CRCMSG( "-----------------------------------------------------\n" );
  1438. CRCMSG( "-------------------CRC=%08X----------------------\n", result );
  1439. CRCMSG( "-----------------------------------------------------\n" );
  1440. return result;
  1441. }
  1442. SendTable *SendTabe_GetTable(int index)
  1443. {
  1444. return g_SendTables[index];
  1445. }
  1446. int SendTable_GetNum()
  1447. {
  1448. return g_SendTables.Count();
  1449. }
  1450. //-----------------------------------------------------------------------------
  1451. // Purpose:
  1452. // Output : CRC32_t
  1453. //-----------------------------------------------------------------------------
  1454. CRC32_t SendTable_GetCRC()
  1455. {
  1456. return g_SendTableCRC;
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. // Purpose: check integrity of an unpacked entity send table
  1460. //-----------------------------------------------------------------------------
  1461. bool SendTable_CheckIntegrity( SendTable *pTable, const void *pData, const int nDataBits )
  1462. {
  1463. #ifdef _DEBUG
  1464. if ( pData == NULL && nDataBits == 0 )
  1465. return true;
  1466. bf_read bfRead( "SendTable_CheckIntegrity", pData, Bits2Bytes(nDataBits), nDataBits );
  1467. CDeltaBitsReader bitsReader( &bfRead );
  1468. int iProp = PROP_SENTINEL;
  1469. int iLastProp = -1;
  1470. int nMaxProps = pTable->m_pPrecalc->GetNumProps();
  1471. int nPropCount = 0;
  1472. Assert( nMaxProps > 0 && nMaxProps < MAX_DATATABLE_PROPS );
  1473. while( -1 != (iProp = bitsReader.ReadNextPropIndex()) )
  1474. {
  1475. Assert( (iProp>=0) && (iProp<nMaxProps) );
  1476. // must be larger
  1477. Assert( iProp > iLastProp );
  1478. const SendProp *pProp = pTable->m_pPrecalc->GetProp( iProp );
  1479. Assert( pProp );
  1480. // ok check that SkipProp & IsEncodedZero read the same bit length
  1481. int iStartBit = bfRead.GetNumBitsRead();
  1482. g_PropTypeFns[ pProp->GetType() ].SkipProp( pProp, &bfRead );
  1483. int nLength = bfRead.GetNumBitsRead() - iStartBit;
  1484. Assert( nLength > 0 ); // a prop must have some bits
  1485. bfRead.Seek( iStartBit ); // read it again
  1486. g_PropTypeFns[ pProp->GetType() ].IsEncodedZero( pProp, &bfRead );
  1487. Assert( nLength == (bfRead.GetNumBitsRead() - iStartBit) );
  1488. nPropCount++;
  1489. iLastProp = iProp;
  1490. }
  1491. Assert( nPropCount <= nMaxProps );
  1492. Assert( bfRead.GetNumBytesLeft() < 4 );
  1493. Assert( !bfRead.IsOverflowed() );
  1494. #endif
  1495. return true;
  1496. }