Team Fortress 2 Source Code as on 22/4/2020
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.

1045 lines
26 KiB

  1. //========= Copyright 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. // memdbgon must be the last include file in a .cpp file!!!
  19. #include <tier0/memdbgon.h>
  20. extern int host_framecount;
  21. CRC32_t SendTable_ComputeCRC();
  22. class CSendTablePrecalc;
  23. class CSendNode;
  24. extern bool Sendprop_UsingDebugWatch();
  25. // This stack doesn't actually call any proxies. It uses the CSendProxyRecipients to tell
  26. // what can be sent to the specified client.
  27. class CPropCullStack : public CDatatableStack
  28. {
  29. public:
  30. CPropCullStack(
  31. CSendTablePrecalc *pPrecalc,
  32. int iClient,
  33. const CSendProxyRecipients *pOldStateProxies,
  34. const int nOldStateProxies,
  35. const CSendProxyRecipients *pNewStateProxies,
  36. const int nNewStateProxies
  37. ) :
  38. CDatatableStack( pPrecalc, (unsigned char*)1, -1 ),
  39. m_pOldStateProxies( pOldStateProxies ),
  40. m_nOldStateProxies( nOldStateProxies ),
  41. m_pNewStateProxies( pNewStateProxies ),
  42. m_nNewStateProxies( nNewStateProxies )
  43. {
  44. m_pPrecalc = pPrecalc;
  45. m_iClient = iClient;
  46. }
  47. inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
  48. {
  49. if ( pNode->GetDataTableProxyIndex() == DATATABLE_PROXY_INDEX_NOPROXY )
  50. {
  51. return (unsigned char*)1;
  52. }
  53. else
  54. {
  55. Assert( pNode->GetDataTableProxyIndex() < m_nNewStateProxies );
  56. bool bCur = m_pNewStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
  57. if ( m_pOldStateProxies )
  58. {
  59. Assert( pNode->GetDataTableProxyIndex() < m_nOldStateProxies );
  60. bool bPrev = m_pOldStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
  61. if ( bPrev != bCur )
  62. {
  63. if ( bPrev )
  64. {
  65. // Old state had the data and the new state doesn't.
  66. return 0;
  67. }
  68. else
  69. {
  70. // Add ALL properties under this proxy because the previous state didn't have any of them.
  71. for ( int i=0; i < pNode->m_nRecursiveProps; i++ )
  72. {
  73. if ( m_nNewProxyProps < ARRAYSIZE( m_NewProxyProps ) )
  74. {
  75. m_NewProxyProps[m_nNewProxyProps] = pNode->m_iFirstRecursiveProp + i;
  76. }
  77. else
  78. {
  79. Error( "CPropCullStack::CallPropProxy - overflowed m_nNewProxyProps" );
  80. }
  81. ++m_nNewProxyProps;
  82. }
  83. // Tell the outer loop that writes to m_pOutProps not to write anything from this
  84. // proxy since we just did.
  85. return 0;
  86. }
  87. }
  88. }
  89. return (unsigned char*)bCur;
  90. }
  91. }
  92. virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
  93. {
  94. // Remember where the game code pointed us for this datatable's data so
  95. m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
  96. for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
  97. {
  98. CSendNode *pCurChild = pNode->GetChild( iChild );
  99. unsigned char *pNewStructBase = NULL;
  100. if ( pStructBase )
  101. {
  102. pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
  103. }
  104. RecurseAndCallProxies( pCurChild, pNewStructBase );
  105. }
  106. }
  107. inline void AddProp( int iProp )
  108. {
  109. if ( m_nOutProps < m_nMaxOutProps )
  110. {
  111. m_pOutProps[m_nOutProps] = iProp;
  112. }
  113. else
  114. {
  115. Error( "CPropCullStack::AddProp - m_pOutProps overflowed" );
  116. }
  117. ++m_nOutProps;
  118. }
  119. void CullPropsFromProxies( const int *pStartProps, int nStartProps, int *pOutProps, int nMaxOutProps )
  120. {
  121. m_nOutProps = 0;
  122. m_pOutProps = pOutProps;
  123. m_nMaxOutProps = nMaxOutProps;
  124. m_nNewProxyProps = 0;
  125. Init();
  126. // This list will have any newly available props written into it. Write a sentinel at the end.
  127. m_NewProxyProps[m_nNewProxyProps] = -1; // invalid marker
  128. int *pCurNewProxyProp = m_NewProxyProps;
  129. for ( int i=0; i < nStartProps; i++ )
  130. {
  131. int iProp = pStartProps[i];
  132. // Fill in the gaps with any properties that are newly enabled by the proxies.
  133. while ( (unsigned int) *pCurNewProxyProp < (unsigned int) iProp )
  134. {
  135. AddProp( *pCurNewProxyProp );
  136. ++pCurNewProxyProp;
  137. }
  138. // Now write this property's index if the proxies are allowing this property to be written.
  139. if ( IsPropProxyValid( iProp ) )
  140. {
  141. AddProp( iProp );
  142. // avoid that we add it twice.
  143. if ( *pCurNewProxyProp == iProp )
  144. ++pCurNewProxyProp;
  145. }
  146. }
  147. // add any remaining new proxy props
  148. while ( (unsigned int) *pCurNewProxyProp < MAX_DATATABLE_PROPS )
  149. {
  150. AddProp( *pCurNewProxyProp );
  151. ++pCurNewProxyProp;
  152. }
  153. }
  154. int GetNumOutProps()
  155. {
  156. return m_nOutProps;
  157. }
  158. private:
  159. CSendTablePrecalc *m_pPrecalc;
  160. int m_iClient; // Which client it's encoding out for.
  161. const CSendProxyRecipients *m_pOldStateProxies;
  162. const int m_nOldStateProxies;
  163. const CSendProxyRecipients *m_pNewStateProxies;
  164. const int m_nNewStateProxies;
  165. // The output property list.
  166. int *m_pOutProps;
  167. int m_nMaxOutProps;
  168. int m_nOutProps;
  169. int m_NewProxyProps[MAX_DATATABLE_PROPS+1];
  170. int m_nNewProxyProps;
  171. };
  172. // ----------------------------------------------------------------------------- //
  173. // CEncodeInfo
  174. // Used by SendTable_Encode.
  175. // ----------------------------------------------------------------------------- //
  176. class CEncodeInfo : public CServerDatatableStack
  177. {
  178. public:
  179. CEncodeInfo( CSendTablePrecalc *pPrecalc, unsigned char *pStructBase, int objectID, bf_write *pOut ) :
  180. CServerDatatableStack( pPrecalc, pStructBase, objectID ),
  181. m_DeltaBitsWriter( pOut )
  182. {
  183. }
  184. public:
  185. CDeltaBitsWriter m_DeltaBitsWriter;
  186. };
  187. // ------------------------------------------------------------------------ //
  188. // Globals.
  189. // ------------------------------------------------------------------------ //
  190. CUtlVector< SendTable* > g_SendTables;
  191. CRC32_t g_SendTableCRC = 0;
  192. // ------------------------------------------------------------------------ //
  193. // SendTable functions.
  194. // ------------------------------------------------------------------------ //
  195. static bool s_debug_info_shown = false;
  196. static int s_debug_bits_start = 0;
  197. static inline void ShowEncodeDeltaWatchInfo(
  198. const SendTable *pTable,
  199. const SendProp *pProp,
  200. bf_read &buffer,
  201. const int objectID,
  202. const int index )
  203. {
  204. if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) )
  205. return;
  206. static int lastframe = -1;
  207. if ( host_framecount != lastframe )
  208. {
  209. lastframe = host_framecount;
  210. ConDMsg( "delta entity: %i\n", objectID );
  211. }
  212. // work on copy of bitbuffer
  213. bf_read copy = buffer;
  214. s_debug_info_shown = true;
  215. DecodeInfo info;
  216. info.m_pStruct = NULL;
  217. info.m_pData = NULL;
  218. info.m_pRecvProp = NULL;
  219. info.m_pProp = pProp;
  220. info.m_pIn = &copy;
  221. info.m_Value.m_Type = (SendPropType)pProp->m_Type;
  222. int startBit = copy.GetNumBitsRead();
  223. g_PropTypeFns[pProp->m_Type].Decode( &info );
  224. int bits = copy.GetNumBitsRead() - startBit;
  225. const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString();
  226. const char *value = info.m_Value.ToString();
  227. ConDMsg( "+ %s %s, %s, index %i, bits %i, value %s\n", pTable->GetName(), pProp->GetName(), type, index, bits, value );
  228. }
  229. static FORCEINLINE void SendTable_EncodeProp( CEncodeInfo * pInfo, unsigned long iProp )
  230. {
  231. // Call their proxy to get the property's value.
  232. DVariant var;
  233. const SendProp *pProp = pInfo->GetCurProp();
  234. unsigned char *pStructBase = pInfo->GetCurStructBase();
  235. pProp->GetProxyFn()(
  236. pProp,
  237. pStructBase,
  238. pStructBase + pProp->GetOffset(),
  239. &var,
  240. 0, // iElement
  241. pInfo->GetObjectID()
  242. );
  243. // Write the index.
  244. pInfo->m_DeltaBitsWriter.WritePropIndex( iProp );
  245. g_PropTypeFns[pProp->m_Type].Encode(
  246. pStructBase,
  247. &var,
  248. pProp,
  249. pInfo->m_DeltaBitsWriter.GetBitBuf(),
  250. pInfo->GetObjectID()
  251. );
  252. }
  253. static bool SendTable_IsPropZero( CEncodeInfo *pInfo, unsigned long iProp )
  254. {
  255. const SendProp *pProp = pInfo->GetCurProp();
  256. // Call their proxy to get the property's value.
  257. DVariant var;
  258. unsigned char *pBase = pInfo->GetCurStructBase();
  259. pProp->GetProxyFn()(
  260. pProp,
  261. pBase,
  262. pBase + pProp->GetOffset(),
  263. &var,
  264. 0, // iElement
  265. pInfo->GetObjectID()
  266. );
  267. return g_PropTypeFns[pProp->m_Type].IsZero( pBase, &var, pProp );
  268. }
  269. int SendTable_CullPropsFromProxies(
  270. const SendTable *pTable,
  271. const int *pStartProps,
  272. int nStartProps,
  273. const int iClient,
  274. const CSendProxyRecipients *pOldStateProxies,
  275. const int nOldStateProxies,
  276. const CSendProxyRecipients *pNewStateProxies,
  277. const int nNewStateProxies,
  278. int *pOutProps,
  279. int nMaxOutProps
  280. )
  281. {
  282. Assert( !( nNewStateProxies && !pNewStateProxies ) );
  283. CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies );
  284. stack.CullPropsFromProxies( pStartProps, nStartProps, pOutProps, nMaxOutProps );
  285. ErrorIfNot( stack.GetNumOutProps() <= nMaxOutProps, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) );
  286. return stack.GetNumOutProps();
  287. }
  288. // compares properties and writes delta properties, it ignores reciepients
  289. int SendTable_WriteAllDeltaProps(
  290. const SendTable *pTable,
  291. const void *pFromData,
  292. const int nFromDataBits,
  293. const void *pToData,
  294. const int nToDataBits,
  295. const int nObjectID,
  296. bf_write *pBufOut )
  297. {
  298. // Calculate the delta props.
  299. int deltaProps[MAX_DATATABLE_PROPS];
  300. int nDeltaProps = SendTable_CalcDelta(
  301. pTable,
  302. pFromData,
  303. nFromDataBits,
  304. pToData,
  305. nToDataBits,
  306. deltaProps,
  307. ARRAYSIZE( deltaProps ),
  308. nObjectID );
  309. // Write the properties.
  310. SendTable_WritePropList(
  311. pTable,
  312. pToData, // object data
  313. nToDataBits,
  314. pBufOut, // output buffer
  315. nObjectID,
  316. deltaProps,
  317. nDeltaProps
  318. );
  319. return nDeltaProps;
  320. }
  321. bool SendTable_Encode(
  322. const SendTable *pTable,
  323. const void *pStruct,
  324. bf_write *pOut,
  325. int objectID,
  326. CUtlMemory<CSendProxyRecipients> *pRecipients,
  327. bool bNonZeroOnly
  328. )
  329. {
  330. CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
  331. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) );
  332. if ( pRecipients )
  333. {
  334. ErrorIfNot( pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") );
  335. }
  336. VPROF( "SendTable_Encode" );
  337. CServerDTITimer timer( pTable, SERVERDTI_ENCODE );
  338. // Setup all the info we'll be walking the tree with.
  339. CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID, pOut );
  340. info.m_pRecipients = pRecipients; // optional buffer to store the bits for which clients get what data.
  341. info.Init();
  342. int iNumProps = pPrecalc->GetNumProps();
  343. for ( int iProp=0; iProp < iNumProps; iProp++ )
  344. {
  345. // skip if we don't have a valid prop proxy
  346. if ( !info.IsPropProxyValid( iProp ) )
  347. continue;
  348. info.SeekToProp( iProp );
  349. // skip empty prop if we only encode non-zero values
  350. if ( bNonZeroOnly && SendTable_IsPropZero(&info, iProp) )
  351. continue;
  352. SendTable_EncodeProp( &info, iProp );
  353. }
  354. return !pOut->IsOverflowed();
  355. }
  356. void SendTable_WritePropList(
  357. const SendTable *pTable,
  358. const void *pState,
  359. const int nBits,
  360. bf_write *pOut,
  361. const int objectID,
  362. const int *pCheckProps,
  363. const int nCheckProps
  364. )
  365. {
  366. if ( nCheckProps == 0 )
  367. {
  368. // Write single final zero bit, signifying that there no changed properties
  369. pOut->WriteOneBit( 0 );
  370. return;
  371. }
  372. bool bDebugWatch = Sendprop_UsingDebugWatch();
  373. s_debug_info_shown = false;
  374. s_debug_bits_start = pOut->GetNumBitsWritten();
  375. CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
  376. CDeltaBitsWriter deltaBitsWriter( pOut );
  377. bf_read inputBuffer( "SendTable_WritePropList->inputBuffer", pState, BitByte( nBits ), nBits );
  378. CDeltaBitsReader inputBitsReader( &inputBuffer );
  379. // Ok, they want to specify a small list of properties to check.
  380. unsigned int iToProp = inputBitsReader.ReadNextPropIndex();
  381. int i = 0;
  382. while ( i < nCheckProps )
  383. {
  384. // Seek the 'to' state to the current property we want to check.
  385. while ( iToProp < (unsigned int) pCheckProps[i] )
  386. {
  387. inputBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
  388. iToProp = inputBitsReader.ReadNextPropIndex();
  389. }
  390. if ( iToProp >= MAX_DATATABLE_PROPS )
  391. {
  392. break;
  393. }
  394. if ( iToProp == (unsigned int) pCheckProps[i] )
  395. {
  396. const SendProp *pProp = pPrecalc->GetProp( iToProp );
  397. // Show debug stuff.
  398. if ( bDebugWatch )
  399. {
  400. ShowEncodeDeltaWatchInfo( pTable, pProp, inputBuffer, objectID, iToProp );
  401. }
  402. // See how many bits the data for this property takes up.
  403. int nToStateBits;
  404. int iStartBit = pOut->GetNumBitsWritten();
  405. deltaBitsWriter.WritePropIndex( iToProp );
  406. inputBitsReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pProp );
  407. nToStateBits = pOut->GetNumBitsWritten() - iStartBit;
  408. TRACE_PACKET( ( " Send Field (%s) = %d (%d bytes)\n", pProp->GetName(), nToStateBits, ( nToStateBits + 7 ) / 8 ) );
  409. // Seek to the next prop.
  410. iToProp = inputBitsReader.ReadNextPropIndex();
  411. }
  412. ++i;
  413. }
  414. if ( s_debug_info_shown )
  415. {
  416. int bits = pOut->GetNumBitsWritten() - s_debug_bits_start;
  417. ConDMsg( "= %i bits (%i bytes)\n", bits, Bits2Bytes(bits) );
  418. }
  419. inputBitsReader.ForceFinished(); // avoid a benign assert
  420. }
  421. int SendTable_CalcDelta(
  422. const SendTable *pTable,
  423. const void *pFromState,
  424. const int nFromBits,
  425. const void *pToState,
  426. const int nToBits,
  427. int *pDeltaProps,
  428. int nMaxDeltaProps,
  429. const int objectID
  430. )
  431. {
  432. CServerDTITimer timer( pTable, SERVERDTI_CALCDELTA );
  433. int *pDeltaPropsBase = pDeltaProps;
  434. int *pDeltaPropsEnd = pDeltaProps + nMaxDeltaProps;
  435. VPROF( "SendTable_CalcDelta" );
  436. // Trivial reject.
  437. //if ( CompareBitArrays( pFromState, pToState, nFromBits, nToBits ) )
  438. //{
  439. // return 0;
  440. //}
  441. CSendTablePrecalc* pPrecalc = pTable->m_pPrecalc;
  442. bf_read toBits( "SendTable_CalcDelta/toBits", pToState, BitByte(nToBits), nToBits );
  443. CDeltaBitsReader toBitsReader( &toBits );
  444. unsigned int iToProp = toBitsReader.ReadNextPropIndex();
  445. if ( pFromState )
  446. {
  447. bf_read fromBits( "SendTable_CalcDelta/fromBits", pFromState, BitByte(nFromBits), nFromBits );
  448. CDeltaBitsReader fromBitsReader( &fromBits );
  449. unsigned int iFromProp = fromBitsReader.ReadNextPropIndex();
  450. for ( ; iToProp < MAX_DATATABLE_PROPS; iToProp = toBitsReader.ReadNextPropIndex() )
  451. {
  452. Assert( (int)iToProp >= 0 );
  453. // Skip any properties in the from state that aren't in the to state.
  454. while ( iFromProp < iToProp )
  455. {
  456. fromBitsReader.SkipPropData( pPrecalc->GetProp( iFromProp ) );
  457. iFromProp = fromBitsReader.ReadNextPropIndex();
  458. }
  459. if ( iFromProp == iToProp )
  460. {
  461. // The property is in both states, so compare them and write the index
  462. // if the states are different.
  463. if ( fromBitsReader.ComparePropData( &toBitsReader, pPrecalc->GetProp( iToProp ) ) )
  464. {
  465. *pDeltaProps++ = iToProp;
  466. if ( pDeltaProps >= pDeltaPropsEnd )
  467. {
  468. break;
  469. }
  470. }
  471. // Seek to the next property.
  472. iFromProp = fromBitsReader.ReadNextPropIndex();
  473. }
  474. else
  475. {
  476. // Only the 'to' state has this property, so just skip its data and register a change.
  477. toBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
  478. *pDeltaProps++ = iToProp;
  479. if ( pDeltaProps >= pDeltaPropsEnd )
  480. {
  481. break;
  482. }
  483. }
  484. }
  485. Assert( iToProp == ~0u );
  486. fromBitsReader.ForceFinished();
  487. }
  488. else
  489. {
  490. for ( ; iToProp != (uint)-1; iToProp = toBitsReader.ReadNextPropIndex() )
  491. {
  492. Assert( (int)iToProp >= 0 && iToProp < MAX_DATATABLE_PROPS );
  493. const SendProp *pProp = pPrecalc->GetProp( iToProp );
  494. if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &toBits ) )
  495. {
  496. *pDeltaProps++ = iToProp;
  497. if ( pDeltaProps >= pDeltaPropsEnd )
  498. {
  499. break;
  500. }
  501. }
  502. }
  503. }
  504. // Return the # of properties that changed between 'from' and 'to'.
  505. return pDeltaProps - pDeltaPropsBase;
  506. }
  507. bool SendTable_WriteInfos( SendTable *pTable, bf_write *pBuf )
  508. {
  509. pBuf->WriteString( pTable->GetName() );
  510. pBuf->WriteUBitLong( pTable->GetNumProps(), PROPINFOBITS_NUMPROPS );
  511. // Send each property.
  512. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  513. {
  514. const SendProp *pProp = &pTable->m_pProps[iProp];
  515. pBuf->WriteUBitLong( (unsigned int)pProp->m_Type, PROPINFOBITS_TYPE );
  516. pBuf->WriteString( pProp->GetName() );
  517. // we now have some flags that aren't networked so strip them off
  518. unsigned int networkFlags = pProp->GetFlags() & ((1<<PROPINFOBITS_FLAGS)-1);
  519. pBuf->WriteUBitLong( networkFlags, PROPINFOBITS_FLAGS );
  520. if( pProp->m_Type == DPT_DataTable )
  521. {
  522. // Just write the name and it will be able to reuse the table with a matching name.
  523. pBuf->WriteString( pProp->GetDataTable()->m_pNetTableName );
  524. }
  525. else
  526. {
  527. if ( pProp->IsExcludeProp() )
  528. {
  529. pBuf->WriteString( pProp->GetExcludeDTName() );
  530. }
  531. else if ( pProp->GetType() == DPT_Array )
  532. {
  533. pBuf->WriteUBitLong( pProp->GetNumElements(), PROPINFOBITS_NUMELEMENTS );
  534. }
  535. else
  536. {
  537. pBuf->WriteBitFloat( pProp->m_fLowValue );
  538. pBuf->WriteBitFloat( pProp->m_fHighValue );
  539. pBuf->WriteUBitLong( pProp->m_nBits, PROPINFOBITS_NUMBITS );
  540. }
  541. }
  542. }
  543. return !pBuf->IsOverflowed();
  544. }
  545. // Spits out warnings for invalid properties and forces property values to
  546. // be in valid ranges for the encoders and decoders.
  547. static void SendTable_Validate( CSendTablePrecalc *pPrecalc )
  548. {
  549. SendTable *pTable = pPrecalc->m_pSendTable;
  550. for( int i=0; i < pTable->m_nProps; i++ )
  551. {
  552. SendProp *pProp = &pTable->m_pProps[i];
  553. if ( pProp->GetArrayProp() )
  554. {
  555. if ( pProp->GetArrayProp()->GetType() == DPT_DataTable )
  556. {
  557. 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() );
  558. }
  559. }
  560. else
  561. {
  562. ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) );
  563. }
  564. // Check for 1-bit signed properties (their value doesn't get down to the client).
  565. if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) )
  566. {
  567. 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());
  568. }
  569. }
  570. for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
  571. {
  572. const SendProp *pProp = pPrecalc->GetProp( i );
  573. if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
  574. {
  575. pTable->SetHasPropsEncodedAgainstTickcount( true );
  576. break;
  577. }
  578. }
  579. }
  580. static void SendTable_CalcNextVectorElems( SendTable *pTable )
  581. {
  582. for ( int i=0; i < pTable->GetNumProps(); i++ )
  583. {
  584. SendProp *pProp = pTable->GetProp( i );
  585. if ( pProp->GetType() == DPT_DataTable )
  586. {
  587. SendTable_CalcNextVectorElems( pProp->GetDataTable() );
  588. }
  589. else if ( pProp->GetOffset() < 0 )
  590. {
  591. pProp->SetOffset( -pProp->GetOffset() );
  592. pProp->SetFlags( pProp->GetFlags() | SPROP_IS_A_VECTOR_ELEM );
  593. }
  594. }
  595. }
  596. static bool SendTable_InitTable( SendTable *pTable )
  597. {
  598. if( pTable->m_pPrecalc )
  599. return true;
  600. // Create the CSendTablePrecalc.
  601. CSendTablePrecalc *pPrecalc = new CSendTablePrecalc;
  602. pTable->m_pPrecalc = pPrecalc;
  603. pPrecalc->m_pSendTable = pTable;
  604. pTable->m_pPrecalc = pPrecalc;
  605. SendTable_CalcNextVectorElems( pTable );
  606. // Bind the instrumentation if -dti was specified.
  607. pPrecalc->m_pDTITable = ServerDTI_HookTable( pTable );
  608. // Setup its flat property array.
  609. if ( !pPrecalc->SetupFlatPropertyArray() )
  610. return false;
  611. SendTable_Validate( pPrecalc );
  612. return true;
  613. }
  614. static void SendTable_TermTable( SendTable *pTable )
  615. {
  616. if( !pTable->m_pPrecalc )
  617. return;
  618. delete pTable->m_pPrecalc;
  619. Assert( !pTable->m_pPrecalc ); // Make sure it unbound itself.
  620. }
  621. int SendTable_GetNumFlatProps( SendTable *pSendTable )
  622. {
  623. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  624. ErrorIfNot( pPrecalc,
  625. ("SendTable_GetNumFlatProps: missing pPrecalc.")
  626. );
  627. return pPrecalc->GetNumProps();
  628. }
  629. CRC32_t SendTable_CRCTable( CRC32_t &crc, SendTable *pTable )
  630. {
  631. CRC32_ProcessBuffer( &crc, (void *)pTable->m_pNetTableName, Q_strlen( pTable->m_pNetTableName) );
  632. int nProps = LittleLong( pTable->m_nProps );
  633. CRC32_ProcessBuffer( &crc, (void *)&nProps, sizeof( pTable->m_nProps ) );
  634. // Send each property.
  635. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  636. {
  637. const SendProp *pProp = &pTable->m_pProps[iProp];
  638. int type = LittleLong( pProp->m_Type );
  639. CRC32_ProcessBuffer( &crc, (void *)&type, sizeof( type ) );
  640. CRC32_ProcessBuffer( &crc, (void *)pProp->GetName() , Q_strlen( pProp->GetName() ) );
  641. int flags = LittleLong( pProp->GetFlags() );
  642. CRC32_ProcessBuffer( &crc, (void *)&flags, sizeof( flags ) );
  643. if( pProp->m_Type == DPT_DataTable )
  644. {
  645. CRC32_ProcessBuffer( &crc, (void *)pProp->GetDataTable()->m_pNetTableName, Q_strlen( pProp->GetDataTable()->m_pNetTableName ) );
  646. }
  647. else
  648. {
  649. if ( pProp->IsExcludeProp() )
  650. {
  651. CRC32_ProcessBuffer( &crc, (void *)pProp->GetExcludeDTName(), Q_strlen( pProp->GetExcludeDTName() ) );
  652. }
  653. else if ( pProp->GetType() == DPT_Array )
  654. {
  655. int numelements = LittleLong( pProp->GetNumElements() );
  656. CRC32_ProcessBuffer( &crc, (void *)&numelements, sizeof( numelements ) );
  657. }
  658. else
  659. {
  660. float lowvalue;
  661. LittleFloat( &lowvalue, &pProp->m_fLowValue );
  662. CRC32_ProcessBuffer( &crc, (void *)&lowvalue, sizeof( lowvalue ) );
  663. float highvalue;
  664. LittleFloat( &highvalue, &pProp->m_fHighValue );
  665. CRC32_ProcessBuffer( &crc, (void *)&highvalue, sizeof( highvalue ) );
  666. int bits = LittleLong( pProp->m_nBits );
  667. CRC32_ProcessBuffer( &crc, (void *)&bits, sizeof( bits ) );
  668. }
  669. }
  670. }
  671. return crc;
  672. }
  673. void SendTable_PrintStats( void )
  674. {
  675. int numTables = 0;
  676. int numFloats = 0;
  677. int numStrings = 0;
  678. int numArrays = 0;
  679. int numInts = 0;
  680. int numVecs = 0;
  681. int numVecXYs = 0;
  682. int numSubTables = 0;
  683. int numSendProps = 0;
  684. int numFlatProps = 0;
  685. int numExcludeProps = 0;
  686. for ( int i=0; i < g_SendTables.Count(); i++ )
  687. {
  688. SendTable *st = g_SendTables[i];
  689. numTables++;
  690. numSendProps += st->GetNumProps();
  691. numFlatProps += st->m_pPrecalc->GetNumProps();
  692. for ( int j=0; j < st->GetNumProps(); j++ )
  693. {
  694. SendProp* sp = st->GetProp( j );
  695. if ( sp->IsExcludeProp() )
  696. {
  697. numExcludeProps++;
  698. continue; // no real sendprops
  699. }
  700. if ( sp->IsInsideArray() )
  701. continue;
  702. switch( sp->GetType() )
  703. {
  704. case DPT_Int : numInts++; break;
  705. case DPT_Float : numFloats++; break;
  706. case DPT_Vector : numVecs++; break;
  707. case DPT_VectorXY : numVecXYs++; break;
  708. case DPT_String : numStrings++; break;
  709. case DPT_Array : numArrays++; break;
  710. case DPT_DataTable : numSubTables++; break;
  711. }
  712. }
  713. }
  714. Msg("Total Send Table stats\n");
  715. Msg("Send Tables : %i\n", numTables );
  716. Msg("Send Props : %i\n", numSendProps );
  717. Msg("Flat Props : %i\n", numFlatProps );
  718. Msg("Int Props : %i\n", numInts );
  719. Msg("Float Props : %i\n", numFloats );
  720. Msg("Vector Props : %i\n", numVecs );
  721. Msg("VectorXY Props: %i\n", numVecXYs );
  722. Msg("String Props : %i\n", numStrings );
  723. Msg("Array Props : %i\n", numArrays );
  724. Msg("Table Props : %i\n", numSubTables );
  725. Msg("Exclu Props : %i\n", numExcludeProps );
  726. }
  727. bool SendTable_Init( SendTable **pTables, int nTables )
  728. {
  729. ErrorIfNot( g_SendTables.Count() == 0,
  730. ("SendTable_Init: called twice.")
  731. );
  732. // Initialize them all.
  733. for ( int i=0; i < nTables; i++ )
  734. {
  735. if ( !SendTable_InitTable( pTables[i] ) )
  736. return false;
  737. }
  738. // Store off the SendTable list.
  739. g_SendTables.CopyArray( pTables, nTables );
  740. g_SendTableCRC = SendTable_ComputeCRC( );
  741. if ( CommandLine()->FindParm("-dti" ) )
  742. {
  743. SendTable_PrintStats();
  744. }
  745. return true;
  746. }
  747. void SendTable_Term()
  748. {
  749. // Term all the SendTables.
  750. for ( int i=0; i < g_SendTables.Count(); i++ )
  751. SendTable_TermTable( g_SendTables[i] );
  752. // Clear the list of SendTables.
  753. g_SendTables.Purge();
  754. g_SendTableCRC = 0;
  755. }
  756. //-----------------------------------------------------------------------------
  757. // Purpose: Computes the crc for all sendtables for the data sent in the class/table definitions
  758. // Output : CRC32_t
  759. //-----------------------------------------------------------------------------
  760. CRC32_t SendTable_ComputeCRC()
  761. {
  762. CRC32_t result;
  763. CRC32_Init( &result );
  764. // walk the tables and checksum them
  765. int c = g_SendTables.Count();
  766. for ( int i = 0 ; i < c; i++ )
  767. {
  768. SendTable *st = g_SendTables[ i ];
  769. result = SendTable_CRCTable( result, st );
  770. }
  771. CRC32_Final( &result );
  772. return result;
  773. }
  774. SendTable *SendTabe_GetTable(int index)
  775. {
  776. return g_SendTables[index];
  777. }
  778. int SendTable_GetNum()
  779. {
  780. return g_SendTables.Count();
  781. }
  782. //-----------------------------------------------------------------------------
  783. // Purpose:
  784. // Output : CRC32_t
  785. //-----------------------------------------------------------------------------
  786. CRC32_t SendTable_GetCRC()
  787. {
  788. return g_SendTableCRC;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // Purpose: check integrity of an unpacked entity send table
  792. //-----------------------------------------------------------------------------
  793. bool SendTable_CheckIntegrity( SendTable *pTable, const void *pData, const int nDataBits )
  794. {
  795. #ifdef _DEBUG
  796. if ( pData == NULL && nDataBits == 0 )
  797. return true;
  798. bf_read bfRead( "SendTable_CheckIntegrity", pData, Bits2Bytes(nDataBits), nDataBits );
  799. CDeltaBitsReader bitsReader( &bfRead );
  800. int iProp = -1;
  801. int iLastProp = -1;
  802. int nMaxProps = pTable->m_pPrecalc->GetNumProps();
  803. int nPropCount = 0;
  804. Assert( nMaxProps > 0 && nMaxProps < MAX_DATATABLE_PROPS );
  805. while( -1 != (iProp = bitsReader.ReadNextPropIndex()) )
  806. {
  807. Assert( (iProp>=0) && (iProp<nMaxProps) );
  808. // must be larger
  809. Assert( iProp > iLastProp );
  810. const SendProp *pProp = pTable->m_pPrecalc->GetProp( iProp );
  811. Assert( pProp );
  812. // ok check that SkipProp & IsEncodedZero read the same bit length
  813. int iStartBit = bfRead.GetNumBitsRead();
  814. g_PropTypeFns[ pProp->GetType() ].SkipProp( pProp, &bfRead );
  815. int nLength = bfRead.GetNumBitsRead() - iStartBit;
  816. Assert( nLength > 0 ); // a prop must have some bits
  817. bfRead.Seek( iStartBit ); // read it again
  818. g_PropTypeFns[ pProp->GetType() ].IsEncodedZero( pProp, &bfRead );
  819. Assert( nLength == (bfRead.GetNumBitsRead() - iStartBit) );
  820. nPropCount++;
  821. iLastProp = iProp;
  822. }
  823. Assert( nPropCount <= nMaxProps );
  824. Assert( bfRead.GetNumBytesLeft() < 4 );
  825. Assert( !bfRead.IsOverflowed() );
  826. #endif
  827. return true;
  828. }