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.

567 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdarg.h>
  8. #include "dt_send.h"
  9. #include "dt.h"
  10. #include "dt_recv.h"
  11. #include "dt_encode.h"
  12. #include "convar.h"
  13. #include "commonmacros.h"
  14. #include "tier1/strtools.h"
  15. #include "tier0/dbg.h"
  16. #include "dt_stack.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. #define PROPINDEX_NUMBITS 12
  20. #define MAX_TOTAL_SENDTABLE_PROPS (1 << PROPINDEX_NUMBITS)
  21. ConVar g_CV_DTWatchEnt( "dtwatchent", "-1", 0, "Watch this entities data table encoding." );
  22. ConVar g_CV_DTWatchVar( "dtwatchvar", "", 0, "Watch the named variable." );
  23. ConVar g_CV_DTWarning( "dtwarning", "0", 0, "Print data table warnings?" );
  24. ConVar g_CV_DTWatchClass( "dtwatchclass", "", 0, "Watch all fields encoded with this table." );
  25. // ----------------------------------------------------------------------------- //
  26. //
  27. // CBuildHierarchyStruct
  28. //
  29. // Used while building a CSendNode hierarchy.
  30. //
  31. // ----------------------------------------------------------------------------- //
  32. class CBuildHierarchyStruct
  33. {
  34. public:
  35. const ExcludeProp *m_pExcludeProps;
  36. int m_nExcludeProps;
  37. const SendProp *m_pDatatableProps[MAX_TOTAL_SENDTABLE_PROPS];
  38. int m_nDatatableProps;
  39. const SendProp *m_pProps[MAX_TOTAL_SENDTABLE_PROPS];
  40. unsigned char m_PropProxyIndices[MAX_TOTAL_SENDTABLE_PROPS];
  41. int m_nProps;
  42. unsigned char m_nPropProxies;
  43. };
  44. // ----------------------------------------------------------------------------- //
  45. // CSendNode.
  46. // ----------------------------------------------------------------------------- //
  47. CSendNode::CSendNode()
  48. {
  49. m_iDatatableProp = -1;
  50. m_pTable = NULL;
  51. m_iFirstRecursiveProp = m_nRecursiveProps = 0;
  52. m_DataTableProxyIndex = DATATABLE_PROXY_INDEX_INVALID; // set it to a questionable value.
  53. }
  54. CSendNode::~CSendNode()
  55. {
  56. int c = GetNumChildren();
  57. for ( int i = c - 1 ; i >= 0 ; i-- )
  58. {
  59. delete GetChild( i );
  60. }
  61. m_Children.Purge();
  62. }
  63. // ----------------------------------------------------------------------------- //
  64. // CSendTablePrecalc
  65. // ----------------------------------------------------------------------------- //
  66. bool PropOffsetLT( const unsigned short &a, const unsigned short &b )
  67. {
  68. return a < b;
  69. }
  70. CSendTablePrecalc::CSendTablePrecalc() :
  71. m_PropOffsetToIndexMap( 0, 0, PropOffsetLT )
  72. {
  73. m_pDTITable = NULL;
  74. m_pSendTable = 0;
  75. m_nDataTableProxies = 0;
  76. }
  77. CSendTablePrecalc::~CSendTablePrecalc()
  78. {
  79. if ( m_pSendTable )
  80. m_pSendTable->m_pPrecalc = 0;
  81. }
  82. const ExcludeProp* FindExcludeProp(
  83. char const *pTableName,
  84. char const *pPropName,
  85. const ExcludeProp *pExcludeProps,
  86. int nExcludeProps)
  87. {
  88. for ( int i=0; i < nExcludeProps; i++ )
  89. {
  90. if ( stricmp(pExcludeProps[i].m_pTableName, pTableName) == 0 && stricmp(pExcludeProps[i].m_pPropName, pPropName ) == 0 )
  91. return &pExcludeProps[i];
  92. }
  93. return NULL;
  94. }
  95. // Fill in a list of all the excluded props.
  96. static bool SendTable_GetPropsExcluded( const SendTable *pTable, ExcludeProp *pExcludeProps, int &nExcludeProps, int nMaxExcludeProps )
  97. {
  98. for(int i=0; i < pTable->m_nProps; i++)
  99. {
  100. SendProp *pProp = &pTable->m_pProps[i];
  101. if ( pProp->IsExcludeProp() )
  102. {
  103. char const *pName = pProp->GetExcludeDTName();
  104. ErrorIfNot( pName,
  105. ("Found an exclude prop missing a name.")
  106. );
  107. ErrorIfNot( nExcludeProps < nMaxExcludeProps,
  108. ("SendTable_GetPropsExcluded: Overflowed max exclude props with %s.", pName)
  109. );
  110. pExcludeProps[nExcludeProps].m_pTableName = pName;
  111. pExcludeProps[nExcludeProps].m_pPropName = pProp->GetName();
  112. nExcludeProps++;
  113. }
  114. else if ( pProp->GetDataTable() )
  115. {
  116. if( !SendTable_GetPropsExcluded( pProp->GetDataTable(), pExcludeProps, nExcludeProps, nMaxExcludeProps ) )
  117. return false;
  118. }
  119. }
  120. return true;
  121. }
  122. // Set the datatable proxy indices in all datatable SendProps.
  123. static void SetDataTableProxyIndices_R(
  124. CSendTablePrecalc *pMainTable,
  125. CSendNode *pCurTable,
  126. CBuildHierarchyStruct *bhs )
  127. {
  128. for ( int i=0; i < pCurTable->GetNumChildren(); i++ )
  129. {
  130. CSendNode *pNode = pCurTable->GetChild( i );
  131. const SendProp *pProp = bhs->m_pDatatableProps[pNode->m_iDatatableProp];
  132. if ( pProp->GetFlags() & SPROP_PROXY_ALWAYS_YES )
  133. {
  134. pNode->SetDataTableProxyIndex( DATATABLE_PROXY_INDEX_NOPROXY );
  135. }
  136. else
  137. {
  138. pNode->SetDataTableProxyIndex( pMainTable->GetNumDataTableProxies() );
  139. pMainTable->SetNumDataTableProxies( pMainTable->GetNumDataTableProxies() + 1 );
  140. }
  141. SetDataTableProxyIndices_R( pMainTable, pNode, bhs );
  142. }
  143. }
  144. // Set the datatable proxy indices in all datatable SendProps.
  145. static void SetRecursiveProxyIndices_R(
  146. SendTable *pBaseTable,
  147. CSendNode *pCurTable,
  148. int &iCurProxyIndex )
  149. {
  150. if ( iCurProxyIndex >= CDatatableStack::MAX_PROXY_RESULTS )
  151. Error( "Too many proxies for datatable %s.", pBaseTable->GetName() );
  152. pCurTable->SetRecursiveProxyIndex( iCurProxyIndex );
  153. iCurProxyIndex++;
  154. for ( int i=0; i < pCurTable->GetNumChildren(); i++ )
  155. {
  156. CSendNode *pNode = pCurTable->GetChild( i );
  157. SetRecursiveProxyIndices_R( pBaseTable, pNode, iCurProxyIndex );
  158. }
  159. }
  160. void SendTable_BuildHierarchy(
  161. CSendNode *pNode,
  162. const SendTable *pTable,
  163. CBuildHierarchyStruct *bhs
  164. );
  165. void SendTable_BuildHierarchy_IterateProps(
  166. CSendNode *pNode,
  167. const SendTable *pTable,
  168. CBuildHierarchyStruct *bhs,
  169. const SendProp *pNonDatatableProps[MAX_TOTAL_SENDTABLE_PROPS],
  170. int &nNonDatatableProps )
  171. {
  172. int i;
  173. for ( i=0; i < pTable->m_nProps; i++ )
  174. {
  175. const SendProp *pProp = &pTable->m_pProps[i];
  176. if ( pProp->IsExcludeProp() ||
  177. pProp->IsInsideArray() ||
  178. FindExcludeProp( pTable->GetName(), pProp->GetName(), bhs->m_pExcludeProps, bhs->m_nExcludeProps ) )
  179. {
  180. continue;
  181. }
  182. if ( pProp->GetType() == DPT_DataTable )
  183. {
  184. if ( pProp->GetFlags() & SPROP_COLLAPSIBLE )
  185. {
  186. // This is a base class.. no need to make a new CSendNode (and trigger a bunch of
  187. // unnecessary send proxy calls in the datatable stacks).
  188. SendTable_BuildHierarchy_IterateProps(
  189. pNode,
  190. pProp->GetDataTable(),
  191. bhs,
  192. pNonDatatableProps,
  193. nNonDatatableProps );
  194. }
  195. else
  196. {
  197. // Setup a child datatable reference.
  198. CSendNode *pChild = new CSendNode;
  199. // Setup a datatable prop for this node to reference (so the recursion
  200. // routines can get at the proxy).
  201. if ( bhs->m_nDatatableProps >= ARRAYSIZE( bhs->m_pDatatableProps ) )
  202. Error( "Overflowed datatable prop list in SendTable '%s'.", pTable->GetName() );
  203. bhs->m_pDatatableProps[bhs->m_nDatatableProps] = pProp;
  204. pChild->m_iDatatableProp = bhs->m_nDatatableProps;
  205. ++bhs->m_nDatatableProps;
  206. pNode->m_Children.AddToTail( pChild );
  207. // Recurse into the new child datatable.
  208. SendTable_BuildHierarchy( pChild, pProp->GetDataTable(), bhs );
  209. }
  210. }
  211. else
  212. {
  213. if ( nNonDatatableProps >= MAX_TOTAL_SENDTABLE_PROPS )
  214. Error( "SendTable_BuildHierarchy: overflowed non-datatable props with '%s'.", pProp->GetName() );
  215. pNonDatatableProps[nNonDatatableProps] = pProp;
  216. ++nNonDatatableProps;
  217. }
  218. }
  219. }
  220. void SendTable_BuildHierarchy(
  221. CSendNode *pNode,
  222. const SendTable *pTable,
  223. CBuildHierarchyStruct *bhs
  224. )
  225. {
  226. pNode->m_pTable = pTable;
  227. pNode->m_iFirstRecursiveProp = bhs->m_nProps;
  228. Assert( bhs->m_nPropProxies < 255 );
  229. unsigned char curPropProxy = bhs->m_nPropProxies;
  230. ++bhs->m_nPropProxies;
  231. const SendProp *pNonDatatableProps[MAX_TOTAL_SENDTABLE_PROPS];
  232. int nNonDatatableProps = 0;
  233. // First add all the child datatables.
  234. SendTable_BuildHierarchy_IterateProps(
  235. pNode,
  236. pTable,
  237. bhs,
  238. pNonDatatableProps,
  239. nNonDatatableProps );
  240. // Now add the properties.
  241. // Make sure there's room, then just copy the pointers from the loop above.
  242. ErrorIfNot( bhs->m_nProps + nNonDatatableProps < ARRAYSIZE( bhs->m_pProps ),
  243. ("SendTable_BuildHierarchy: overflowed prop buffer.")
  244. );
  245. for ( int i=0; i < nNonDatatableProps; i++ )
  246. {
  247. bhs->m_pProps[bhs->m_nProps] = pNonDatatableProps[i];
  248. bhs->m_PropProxyIndices[bhs->m_nProps] = curPropProxy;
  249. ++bhs->m_nProps;
  250. }
  251. pNode->m_nRecursiveProps = bhs->m_nProps - pNode->m_iFirstRecursiveProp;
  252. }
  253. void SendTable_SortByPriority(CBuildHierarchyStruct *bhs)
  254. {
  255. int i, start = 0;
  256. while( true )
  257. {
  258. for ( i = start; i < bhs->m_nProps; i++ )
  259. {
  260. const SendProp *p = bhs->m_pProps[i];
  261. unsigned char c = bhs->m_PropProxyIndices[i];
  262. if ( p->GetFlags() & SPROP_CHANGES_OFTEN )
  263. {
  264. bhs->m_pProps[i] = bhs->m_pProps[start];
  265. bhs->m_PropProxyIndices[i] = bhs->m_PropProxyIndices[start];
  266. bhs->m_pProps[start] = p;
  267. bhs->m_PropProxyIndices[start] = c;
  268. start++;
  269. break;
  270. }
  271. }
  272. if ( i == bhs->m_nProps )
  273. return;
  274. }
  275. }
  276. void CalcPathLengths_R( CSendNode *pNode, CUtlVector<int> &pathLengths, int curPathLength, int &totalPathLengths )
  277. {
  278. pathLengths[pNode->GetRecursiveProxyIndex()] = curPathLength;
  279. totalPathLengths += curPathLength;
  280. for ( int i=0; i < pNode->GetNumChildren(); i++ )
  281. {
  282. CalcPathLengths_R( pNode->GetChild( i ), pathLengths, curPathLength+1, totalPathLengths );
  283. }
  284. }
  285. void FillPathEntries_R( CSendTablePrecalc *pPrecalc, CSendNode *pNode, CSendNode *pParent, int &iCurEntry )
  286. {
  287. // Fill in this node's path.
  288. CSendTablePrecalc::CProxyPath &outProxyPath = pPrecalc->m_ProxyPaths[ pNode->GetRecursiveProxyIndex() ];
  289. outProxyPath.m_iFirstEntry = (unsigned short)iCurEntry;
  290. // Copy all the proxies leading to the parent.
  291. if ( pParent )
  292. {
  293. CSendTablePrecalc::CProxyPath &parentProxyPath = pPrecalc->m_ProxyPaths[pParent->GetRecursiveProxyIndex()];
  294. outProxyPath.m_nEntries = parentProxyPath.m_nEntries + 1;
  295. for ( int i=0; i < parentProxyPath.m_nEntries; i++ )
  296. pPrecalc->m_ProxyPathEntries[iCurEntry++] = pPrecalc->m_ProxyPathEntries[parentProxyPath.m_iFirstEntry+i];
  297. // Now add this node's own proxy.
  298. pPrecalc->m_ProxyPathEntries[iCurEntry].m_iProxy = pNode->GetRecursiveProxyIndex();
  299. pPrecalc->m_ProxyPathEntries[iCurEntry].m_iDatatableProp = pNode->m_iDatatableProp;
  300. ++iCurEntry;
  301. }
  302. else
  303. {
  304. outProxyPath.m_nEntries = 0;
  305. }
  306. for ( int i=0; i < pNode->GetNumChildren(); i++ )
  307. {
  308. FillPathEntries_R( pPrecalc, pNode->GetChild( i ), pNode, iCurEntry );
  309. }
  310. }
  311. void SendTable_GenerateProxyPaths( CSendTablePrecalc *pPrecalc, int nProxyIndices )
  312. {
  313. // Initialize the array.
  314. pPrecalc->m_ProxyPaths.SetSize( nProxyIndices );
  315. for ( int i=0; i < nProxyIndices; i++ )
  316. pPrecalc->m_ProxyPaths[i].m_iFirstEntry = pPrecalc->m_ProxyPaths[i].m_nEntries = 0xFFFF;
  317. // Figure out how long the path down the tree is to each node.
  318. int totalPathLengths = 0;
  319. CUtlVector<int> pathLengths;
  320. pathLengths.SetSize( nProxyIndices );
  321. memset( pathLengths.Base(), 0, sizeof( pathLengths[0] ) * nProxyIndices );
  322. CalcPathLengths_R( pPrecalc->GetRootNode(), pathLengths, 0, totalPathLengths );
  323. //
  324. int iCurEntry = 0;
  325. pPrecalc->m_ProxyPathEntries.SetSize( totalPathLengths );
  326. FillPathEntries_R( pPrecalc, pPrecalc->GetRootNode(), NULL, iCurEntry );
  327. }
  328. bool CSendTablePrecalc::SetupFlatPropertyArray()
  329. {
  330. SendTable *pTable = GetSendTable();
  331. // First go through and set SPROP_INSIDEARRAY when appropriate, and set array prop pointers.
  332. SetupArrayProps_R<SendTable, SendTable::PropType>( pTable );
  333. // Make a list of which properties are excluded.
  334. ExcludeProp excludeProps[MAX_EXCLUDE_PROPS];
  335. int nExcludeProps = 0;
  336. if( !SendTable_GetPropsExcluded( pTable, excludeProps, nExcludeProps, MAX_EXCLUDE_PROPS ) )
  337. return false;
  338. // Now build the hierarchy.
  339. CBuildHierarchyStruct bhs;
  340. bhs.m_pExcludeProps = excludeProps;
  341. bhs.m_nExcludeProps = nExcludeProps;
  342. bhs.m_nProps = bhs.m_nDatatableProps = 0;
  343. bhs.m_nPropProxies = 0;
  344. SendTable_BuildHierarchy( GetRootNode(), pTable, &bhs );
  345. SendTable_SortByPriority( &bhs );
  346. // Copy the SendProp pointers into the precalc.
  347. MEM_ALLOC_CREDIT();
  348. m_Props.CopyArray( bhs.m_pProps, bhs.m_nProps );
  349. m_DatatableProps.CopyArray( bhs.m_pDatatableProps, bhs.m_nDatatableProps );
  350. m_PropProxyIndices.CopyArray( bhs.m_PropProxyIndices, bhs.m_nProps );
  351. // Assign the datatable proxy indices.
  352. SetNumDataTableProxies( 0 );
  353. SetDataTableProxyIndices_R( this, GetRootNode(), &bhs );
  354. int nProxyIndices = 0;
  355. SetRecursiveProxyIndices_R( pTable, GetRootNode(), nProxyIndices );
  356. SendTable_GenerateProxyPaths( this, nProxyIndices );
  357. return true;
  358. }
  359. // ---------------------------------------------------------------------------------------- //
  360. // Helpers.
  361. // ---------------------------------------------------------------------------------------- //
  362. // Compares two arrays of bits.
  363. // Returns true if they are equal.
  364. bool AreBitArraysEqual(
  365. void const *pvBits1,
  366. void const *pvBits2,
  367. int nBits )
  368. {
  369. unsigned int const *pBits1 = (unsigned int const *)pvBits1;
  370. unsigned int const *pBits2 = (unsigned int const *)pvBits2;
  371. // Compare words.
  372. int nWords = nBits >> 5;
  373. for ( int i = 0 ; i < nWords; ++i )
  374. {
  375. if ( pBits1[i] != pBits2[i] )
  376. return false;
  377. }
  378. if ( nBits & 31 )
  379. {
  380. // Compare remaining bits.
  381. unsigned int mask = (1 << (nBits & 31)) - 1;
  382. return ((pBits1[nWords] ^ pBits2[nWords]) & mask) == 0;
  383. }
  384. return true;
  385. }
  386. // Does a fast memcmp-based test to determine if the two bit arrays are different.
  387. // Returns true if they are equal.
  388. bool CompareBitArrays(
  389. void const *pPacked1,
  390. void const *pPacked2,
  391. int nBits1,
  392. int nBits2
  393. )
  394. {
  395. if( nBits1 >= 0 && nBits1 == nBits2 )
  396. {
  397. if ( pPacked1 == pPacked2 )
  398. {
  399. return true;
  400. }
  401. else
  402. {
  403. return AreBitArraysEqual( pPacked1, pPacked2, nBits1 );
  404. }
  405. }
  406. else
  407. return false;
  408. }
  409. // Looks at the DTWatchEnt and DTWatchProp console variables and returns true
  410. // if the user wants to watch this property.
  411. bool ShouldWatchThisProp( const SendTable *pTable, int objectID, const char *pPropName )
  412. {
  413. if(g_CV_DTWatchEnt.GetInt() != -1 &&
  414. g_CV_DTWatchEnt.GetInt() == objectID)
  415. {
  416. const char *pStr = g_CV_DTWatchVar.GetString();
  417. if ( pStr && pStr[0] != 0 )
  418. {
  419. return stricmp( pStr, pPropName ) == 0;
  420. }
  421. else
  422. {
  423. return true;
  424. }
  425. }
  426. if ( g_CV_DTWatchClass.GetString()[ 0 ] && Q_stristr( pTable->GetName(), g_CV_DTWatchClass.GetString() ) )
  427. return true;
  428. return false;
  429. }
  430. bool Sendprop_UsingDebugWatch()
  431. {
  432. if ( g_CV_DTWatchEnt.GetInt() != -1 )
  433. return true;
  434. if ( g_CV_DTWatchClass.GetString()[ 0 ] )
  435. return true;
  436. return false;
  437. }
  438. // Prints a datatable warning into the console.
  439. void DataTable_Warning( const char *pInMessage, ... )
  440. {
  441. char msg[4096];
  442. va_list marker;
  443. #if 0
  444. #if !defined(_DEBUG)
  445. if(!g_CV_DTWarning.GetInt())
  446. return;
  447. #endif
  448. #endif
  449. va_start(marker, pInMessage);
  450. Q_vsnprintf( msg, sizeof( msg ), pInMessage, marker);
  451. va_end(marker);
  452. Warning( "DataTable warning: %s", msg );
  453. }