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.

912 lines
25 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "dt.h"
  8. #include "dt_recv_eng.h"
  9. #include "dt_encode.h"
  10. #include "dt_instrumentation.h"
  11. #include "dt_stack.h"
  12. #include "utllinkedlist.h"
  13. #include "tier0/dbg.h"
  14. #include "dt_recv_decoder.h"
  15. #include "tier1/strtools.h"
  16. #include "tier0/icommandline.h"
  17. #include "dt_common_eng.h"
  18. #include "common.h"
  19. #include "serializedentity.h"
  20. #include "netmessages.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. class CClientSendTable;
  24. // Testing out this pattern.. you can write simple code blocks inside of
  25. // codeToRun. The thing that sucks is that you can't access your function's
  26. // local variables inside of codeToRun.
  27. //
  28. // If it used an iterator class, it could access local function variables,
  29. // but the iterator class might be more trouble than it's worth.
  30. #define FOR_EACH_PROP_R( TableType, pTablePointer, tableCode, propCode ) \
  31. class CPropVisitor \
  32. { \
  33. public: \
  34. static void Visit_R( TableType *pTable ) \
  35. { \
  36. tableCode; \
  37. \
  38. for ( int i=0; i < pTable->GetNumProps(); i++ ) \
  39. { \
  40. TableType::PropType *pProp = pTable->GetProp( i ); \
  41. \
  42. propCode; \
  43. \
  44. if ( pProp->GetType() == DPT_DataTable ) \
  45. Visit_R( pProp->GetDataTable() ); \
  46. } \
  47. } \
  48. }; \
  49. CPropVisitor::Visit_R( pTablePointer );
  50. #define SENDPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( SendTable, pTablePointer, tableCode, propCode )
  51. #define RECVPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( RecvTable, pTablePointer, tableCode, propCode )
  52. #define SETUP_VISIT() class CDummyClass {} // Workaround for parser bug in VC7.1
  53. // ------------------------------------------------------------------------------------ //
  54. // Globals.
  55. // ------------------------------------------------------------------------------------ //
  56. CUtlLinkedList< RecvTable*, unsigned short > g_RecvTables;
  57. CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders;
  58. CUtlLinkedList< CClientSendTable*, unsigned short > g_ClientSendTables;
  59. // ------------------------------------------------------------------------------------ //
  60. // Static helper functions.
  61. // ------------------------------------------------------------------------------------ //
  62. RecvTable* FindRecvTable( const char *pName )
  63. {
  64. FOR_EACH_LL( g_RecvTables, i )
  65. {
  66. if ( stricmp( g_RecvTables[i]->GetName(), pName ) == 0 )
  67. return g_RecvTables[i];
  68. }
  69. return 0;
  70. }
  71. static CClientSendTable* FindClientSendTable( const char *pName )
  72. {
  73. FOR_EACH_LL( g_ClientSendTables, i )
  74. {
  75. CClientSendTable *pTable = g_ClientSendTables[i];
  76. if ( stricmp( pTable->GetName(), pName ) == 0 )
  77. return pTable;
  78. }
  79. return NULL;
  80. }
  81. // Find all child datatable properties for the send tables.
  82. bool SetupClientSendTableHierarchy()
  83. {
  84. FOR_EACH_LL( g_ClientSendTables, iClientTable )
  85. {
  86. CClientSendTable *pTable = g_ClientSendTables[iClientTable];
  87. // For each datatable property, find the table it references.
  88. for ( int iProp=0; iProp < pTable->GetNumProps(); iProp++ )
  89. {
  90. CClientSendProp *pClientProp = pTable->GetClientProp( iProp );
  91. SendProp *pProp = &pTable->m_SendTable.m_pProps[iProp];
  92. if ( pProp->m_Type == DPT_DataTable )
  93. {
  94. const char *pTableName = pClientProp->GetTableName();
  95. ErrorIfNot( pTableName,
  96. ("SetupClientSendTableHierarchy: missing table name for prop '%s'.", pProp->GetName())
  97. );
  98. CClientSendTable *pChild = FindClientSendTable( pTableName );
  99. if ( !pChild )
  100. {
  101. DataTable_Warning( "SetupClientSendTableHierarchy: missing SendTable '%s' (referenced by '%s').\n", pTableName, pTable->GetName() );
  102. return false;
  103. }
  104. pProp->SetDataTable( &pChild->m_SendTable );
  105. }
  106. }
  107. }
  108. return true;
  109. }
  110. static RecvProp* FindRecvProp( RecvTable *pTable, const char *pName )
  111. {
  112. for ( int i=0; i < pTable->GetNumProps(); i++ )
  113. {
  114. RecvProp *pProp = pTable->GetProp( i );
  115. #ifdef DBGFLAG_ASSERT
  116. // Debug validation to handle that no network fields get created with colliding names to special UTL vector networking fields
  117. // because we will have custom receive table remapping below and don't want to mistakenly route bytes into wrong memory
  118. // See dt_utlvector_recv.cpp / RecvPropUtlVector for details of this remapping
  119. if ( char const *szLength = StringAfterPrefix( pProp->GetName(), "lengthprop" ) )
  120. {
  121. // *pLengthProp = RecvPropInt( AllocateStringHelper( "lengthprop%d", nMaxElements ), 0, 0, 0, RecvProxy_UtlVectorLength );
  122. Assert( pProp->GetType() == DPT_Int );
  123. Assert( *szLength );
  124. for ( char const *szCheck = szLength; szCheck && *szCheck; ++ szCheck )
  125. {
  126. Assert( V_isdigit( *szCheck ) ); // assert that the number forms the length of array
  127. Assert( szCheck - szLength < 5 ); // arrays are never that large!
  128. }
  129. }
  130. else if ( char const *szLPT = StringAfterPrefix( pProp->GetName(), "_LPT_" ) )
  131. {
  132. // char *pLengthProxyTableName = AllocateUniqueDataTableName( false, "_LPT_%s_%d", pVarName, nMaxElements );
  133. Assert( pProp->GetType() == DPT_DataTable );
  134. char const *szLPTsize = strrchr( szLPT, '_' );
  135. Assert( szLPTsize );
  136. if ( szLPTsize )
  137. {
  138. ++ szLPTsize;
  139. Assert( *szLPTsize );
  140. }
  141. for ( char const *szCheck = szLPTsize; szCheck && *szCheck; ++szCheck )
  142. {
  143. Assert( V_isdigit( *szCheck ) ); // assert that the number forms the length of array
  144. Assert( szCheck - szLength < 5 ); // arrays are never that large!
  145. }
  146. }
  147. else if ( char const *szST = StringAfterPrefix( pProp->GetName(), "_ST_" ) )
  148. {
  149. // AllocateUniqueDataTableName( false, "_ST_%s_%d", pVarName, nMaxElements )
  150. Assert( pProp->GetType() == DPT_DataTable );
  151. char const *szSTsize = strrchr( szST, '_' );
  152. Assert( szSTsize );
  153. if ( szSTsize )
  154. {
  155. ++szSTsize;
  156. Assert( *szSTsize );
  157. }
  158. for ( char const *szCheck = szSTsize; szCheck && *szCheck; ++szCheck )
  159. {
  160. Assert( V_isdigit( *szCheck ) ); // assert that the number forms the length of array
  161. Assert( szCheck - szLength < 5 ); // arrays are never that large!
  162. }
  163. }
  164. #endif
  165. if ( !V_stricmp( pProp->GetName(), pName ) )
  166. return pProp;
  167. //
  168. // Special case to receive UTL vector networked prop into a larger UTL vector networked prop on the client
  169. // See dt_utlvector_recv.cpp / RecvPropUtlVector for details of this remapping
  170. //
  171. if ( char const *p_SEND_Length = StringAfterPrefix( pName, "lengthprop" ) )
  172. {
  173. // We are being sent a lengthprop##
  174. if ( char const *p_RECV_Length = StringAfterPrefix( pProp->GetName(), "lengthprop" ) )
  175. {
  176. if ( Q_atoi( p_SEND_Length ) <= Q_atoi( p_RECV_Length ) )
  177. return pProp;
  178. }
  179. }
  180. else if ( char const *p_SEND_LPT = StringAfterPrefix( pName, "_LPT_" ) )
  181. {
  182. // We are being sent an _LPT_(varname)_## field
  183. if ( char const *p_RECV_LPT = StringAfterPrefix( pProp->GetName(), "_LPT_" ) )
  184. {
  185. // Trim the length from the field
  186. char const *p_SEND_LPT_size = strrchr( p_SEND_LPT, '_' );
  187. char const *p_RECV_LPT_size = strrchr( p_RECV_LPT, '_' );
  188. if ( p_SEND_LPT_size && p_RECV_LPT_size &&
  189. ( p_SEND_LPT_size - p_SEND_LPT == p_RECV_LPT_size - p_RECV_LPT ) &&
  190. !V_strnicmp( p_SEND_LPT, p_RECV_LPT, p_RECV_LPT_size - p_RECV_LPT ) &&
  191. ( Q_atoi( p_SEND_LPT_size + 1 ) <= Q_atoi( p_RECV_LPT_size + 1 ) ) )
  192. return pProp;
  193. }
  194. }
  195. else if ( char const *p_SEND_ST = StringAfterPrefix( pName, "_ST_" ) )
  196. {
  197. // We are being sent an _ST_(varname)_## field
  198. if ( char const *p_RECV_ST = StringAfterPrefix( pProp->GetName(), "_ST_" ) )
  199. {
  200. // Trim the length from the field
  201. char const *p_SEND_ST_size = strrchr( p_SEND_ST, '_' );
  202. char const *p_RECV_ST_size = strrchr( p_RECV_ST, '_' );
  203. if ( p_SEND_ST_size && p_RECV_ST_size &&
  204. ( p_SEND_ST_size - p_SEND_ST == p_RECV_ST_size - p_RECV_ST ) &&
  205. !V_strnicmp( p_SEND_ST, p_RECV_ST, p_RECV_ST_size - p_RECV_ST ) &&
  206. ( Q_atoi( p_SEND_ST_size + 1 ) <= Q_atoi( p_RECV_ST_size + 1 ) ) )
  207. return pProp;
  208. }
  209. }
  210. //
  211. // End of UTL vector backwards compatibility receiving remap
  212. //
  213. }
  214. // Support recursing into base classes to find the required field:
  215. if ( pTable->GetNumProps() )
  216. {
  217. RecvProp *pSubProp = pTable->GetProp( 0 );
  218. if ( ( pSubProp->GetType() == DPT_DataTable ) &&
  219. !V_stricmp( pSubProp->GetName(), "baseclass" ) )
  220. return FindRecvProp( pSubProp->GetDataTable(), pName );
  221. }
  222. return NULL;
  223. }
  224. // See if the RecvProp is fit to receive the SendProp's data.
  225. bool CompareRecvPropToSendProp( const RecvProp *pRecvProp, const SendProp *pSendProp )
  226. {
  227. while ( 1 )
  228. {
  229. ErrorIfNot( pRecvProp && pSendProp,
  230. ("CompareRecvPropToSendProp: missing a property.")
  231. );
  232. if ( pRecvProp->GetType() != pSendProp->GetType() || pRecvProp->IsInsideArray() != pSendProp->IsInsideArray() )
  233. {
  234. return false;
  235. }
  236. if ( pRecvProp->GetType() == DPT_Array )
  237. {
  238. // It should be OK to receive into a larger array, just later elements
  239. // will not ever be received
  240. if ( pRecvProp->GetNumElements() < pSendProp->GetNumElements() )
  241. return false;
  242. pRecvProp = pRecvProp->GetArrayProp();
  243. pSendProp = pSendProp->GetArrayProp();
  244. }
  245. else
  246. {
  247. return true;
  248. }
  249. }
  250. }
  251. struct MatchingProp_t
  252. {
  253. SendProp *m_pProp;
  254. RecvProp *m_pMatchingRecvProp;
  255. static bool LessFunc( const MatchingProp_t& lhs, const MatchingProp_t& rhs )
  256. {
  257. return lhs.m_pProp < rhs.m_pProp;
  258. }
  259. };
  260. static bool MatchRecvPropsToSendProps_R( CUtlRBTree< MatchingProp_t, unsigned short >& lookup, char const *sendTableName, SendTable *pSendTable, RecvTable *pRecvTable, bool bAllowMismatches, bool *pAnyMismatches )
  261. {
  262. for ( int i=0; i < pSendTable->m_nProps; i++ )
  263. {
  264. SendProp *pSendProp = &pSendTable->m_pProps[i];
  265. if ( pSendProp->IsExcludeProp() || pSendProp->IsInsideArray() )
  266. continue;
  267. // Find a RecvProp by the same name and type.
  268. RecvProp *pRecvProp = 0;
  269. if ( pRecvTable )
  270. pRecvProp = FindRecvProp( pRecvTable, pSendProp->GetName() );
  271. if ( pRecvProp )
  272. {
  273. if ( !CompareRecvPropToSendProp( pRecvProp, pSendProp ) )
  274. {
  275. Warning( "RecvProp type doesn't match server type for %s/%s\n", pSendTable->GetName(), pSendProp->GetName() );
  276. return false;
  277. }
  278. MatchingProp_t info;
  279. info.m_pProp = pSendProp;
  280. info.m_pMatchingRecvProp = pRecvProp;
  281. lookup.Insert( info );
  282. }
  283. else
  284. {
  285. if ( pAnyMismatches )
  286. {
  287. *pAnyMismatches = true;
  288. }
  289. DevWarning( "Missing RecvProp for %s - %s/%s\n", sendTableName, pSendTable->GetName(), pSendProp->GetName() );
  290. if ( !bAllowMismatches )
  291. {
  292. return false;
  293. }
  294. }
  295. // Recurse.
  296. if ( pSendProp->GetType() == DPT_DataTable )
  297. {
  298. if ( !MatchRecvPropsToSendProps_R( lookup, sendTableName, pSendProp->GetDataTable(), pRecvProp ? pRecvProp->GetDataTable() : 0, bAllowMismatches, pAnyMismatches ) )
  299. return false;
  300. }
  301. }
  302. return true;
  303. }
  304. extern bool s_debug_info_shown;
  305. extern int s_debug_bits_start;
  306. static inline void ShowDecodeDeltaWatchInfo(
  307. char *what,
  308. const RecvTable *pTable,
  309. const SendProp *pProp,
  310. bf_read &buffer,
  311. const int objectID,
  312. const int index )
  313. {
  314. if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) )
  315. return;
  316. extern int host_framecount;
  317. static int lastframe = -1;
  318. if ( host_framecount != lastframe )
  319. {
  320. lastframe = host_framecount;
  321. ConDMsg( "D: delta entity: %i %s\n", objectID, pTable->GetName() );
  322. }
  323. // work on copy of bitbuffer
  324. bf_read copy = buffer;
  325. s_debug_info_shown = true;
  326. DecodeInfo info;
  327. info.m_pStruct = NULL;
  328. info.m_pData = NULL;
  329. info.m_pRecvProp = NULL;
  330. info.m_pProp = pProp;
  331. info.m_pIn = &copy;
  332. info.m_ObjectID = objectID;
  333. info.m_Value.m_Type = (SendPropType)pProp->m_Type;
  334. int startBit = copy.GetNumBitsRead();
  335. g_PropTypeFns[pProp->m_Type].Decode( &info );
  336. int bits = copy.GetNumBitsRead() - startBit;
  337. const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString();
  338. const char *value = info.m_Value.ToString();
  339. ConDMsg( "D[%s]:%s %s, %s, index %i, offset %i, bits %i, value %s\n", what, pTable->GetName(), pProp->GetName(), type, index, startBit, bits, value );
  340. }
  341. // ------------------------------------------------------------------------------------ //
  342. // Interface functions.
  343. // ------------------------------------------------------------------------------------ //
  344. bool RecvTable_Init( RecvTable **pTables, int nTables )
  345. {
  346. SETUP_VISIT();
  347. for ( int i=0; i < nTables; i++ )
  348. {
  349. RECVPROP_VISIT( pTables[i],
  350. {
  351. if ( pTable->IsInMainList() )
  352. return;
  353. // Shouldn't have a decoder yet.
  354. ErrorIfNot( !pTable->m_pDecoder,
  355. ("RecvTable_Init: table '%s' has a decoder already.", pTable->GetName()));
  356. pTable->SetInMainList( true );
  357. g_RecvTables.AddToTail( pTable );
  358. },
  359. {}
  360. );
  361. }
  362. return true;
  363. }
  364. void RecvTable_Term( bool clearall /*= true*/ )
  365. {
  366. DTI_Term();
  367. SETUP_VISIT();
  368. FOR_EACH_LL( g_RecvTables, i )
  369. {
  370. RECVPROP_VISIT( g_RecvTables[i],
  371. {
  372. if ( !pTable->IsInMainList() )
  373. return;
  374. pTable->SetInMainList( false );
  375. pTable->m_pDecoder = 0;
  376. },
  377. {}
  378. );
  379. }
  380. if ( clearall )
  381. {
  382. g_RecvTables.Purge();
  383. }
  384. g_RecvDecoders.PurgeAndDeleteElements();
  385. g_ClientSendTables.PurgeAndDeleteElements();
  386. }
  387. void RecvTable_FreeSendTable( SendTable *pTable )
  388. {
  389. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  390. {
  391. SendProp *pProp = &pTable->m_pProps[iProp];
  392. delete [] pProp->m_pVarName;
  393. if ( pProp->m_pExcludeDTName )
  394. delete [] pProp->m_pExcludeDTName;
  395. }
  396. if ( pTable->m_pProps )
  397. delete [] pTable->m_pProps;
  398. delete [] pTable->m_pNetTableName;
  399. delete pTable;
  400. }
  401. static char* AllocString( const char *pStr )
  402. {
  403. int allocLen = strlen( pStr ) + 1;
  404. char *pOut = new char[allocLen];
  405. V_strncpy( pOut, pStr, allocLen );
  406. return pOut;
  407. }
  408. SendTable *RecvTable_ReadInfos( const CSVCMsg_SendTable& msg, int nDemoProtocol )
  409. {
  410. SendTable *pTable = new SendTable;
  411. pTable->m_pNetTableName = AllocString( msg.net_table_name().c_str() );
  412. // Read the property list.
  413. pTable->m_nProps = msg.props_size();
  414. pTable->m_pProps = pTable->m_nProps ? new SendProp[ pTable->m_nProps ] : NULL;
  415. for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
  416. {
  417. SendProp *pProp = &pTable->m_pProps[iProp];
  418. const CSVCMsg_SendTable::sendprop_t& sendProp = msg.props( iProp );
  419. pProp->m_Type = (SendPropType)sendProp.type();
  420. pProp->m_pVarName = AllocString( sendProp.var_name().c_str() );
  421. pProp->SetFlags( sendProp.flags() );
  422. pProp->SetPriority( sendProp.priority() );
  423. if ( ( pProp->m_Type == DPT_DataTable ) || ( pProp->IsExcludeProp() ) )
  424. {
  425. pProp->m_pExcludeDTName = AllocString( sendProp.dt_name().c_str() );
  426. }
  427. else if ( pProp->GetType() == DPT_Array )
  428. {
  429. pProp->SetNumElements( sendProp.num_elements() );
  430. }
  431. else
  432. {
  433. pProp->m_fLowValue = sendProp.low_value();
  434. pProp->m_fHighValue = sendProp.high_value();
  435. pProp->m_nBits = sendProp.num_bits();
  436. }
  437. }
  438. return pTable;
  439. }
  440. bool RecvTable_RecvClassInfos( const CSVCMsg_SendTable& msg, int nDemoProtocol )
  441. {
  442. SendTable *pSendTable = RecvTable_ReadInfos( msg, nDemoProtocol );
  443. if ( !pSendTable )
  444. return false;
  445. bool ret = DataTable_SetupReceiveTableFromSendTable( pSendTable, msg.needs_decoder() );
  446. RecvTable_FreeSendTable( pSendTable );
  447. return ret;
  448. }
  449. static void CopySendPropsToRecvProps(
  450. CUtlRBTree< MatchingProp_t, unsigned short >& lookup,
  451. const CUtlVector<const SendProp*> &sendProps,
  452. CUtlVector<const RecvProp*> &recvProps
  453. )
  454. {
  455. recvProps.SetSize( sendProps.Count() );
  456. for ( int iSendProp=0; iSendProp < sendProps.Count(); iSendProp++ )
  457. {
  458. const SendProp *pSendProp = sendProps[iSendProp];
  459. MatchingProp_t search;
  460. search.m_pProp = (SendProp *)pSendProp;
  461. int idx = lookup.Find( search );
  462. if ( idx == lookup.InvalidIndex() )
  463. {
  464. recvProps[iSendProp] = 0;
  465. }
  466. else
  467. {
  468. recvProps[iSendProp] = lookup[ idx ].m_pMatchingRecvProp;
  469. }
  470. }
  471. }
  472. bool RecvTable_CreateDecoders( const CStandardSendProxies *pSendProxies, bool bAllowMismatches, bool *pAnyMismatches )
  473. {
  474. DTI_Init();
  475. SETUP_VISIT();
  476. if ( pAnyMismatches )
  477. {
  478. *pAnyMismatches = false;
  479. }
  480. // First, now that we've supposedly received all the SendTables that we need,
  481. // set their datatable child pointers.
  482. if ( !SetupClientSendTableHierarchy() )
  483. return false;
  484. bool bRet = true;
  485. FOR_EACH_LL( g_RecvDecoders, i )
  486. {
  487. CRecvDecoder *pDecoder = g_RecvDecoders[i];
  488. // It should already have been linked to its ClientSendTable.
  489. Assert( pDecoder->m_pClientSendTable );
  490. if ( !pDecoder->m_pClientSendTable )
  491. return false;
  492. // For each decoder, precalculate the SendTable's flat property list.
  493. if ( !pDecoder->m_Precalc.SetupFlatPropertyArray() )
  494. return false;
  495. CUtlRBTree< MatchingProp_t, unsigned short > PropLookup( 0, 0, MatchingProp_t::LessFunc );
  496. // Now match RecvProp with SendProps.
  497. if ( !MatchRecvPropsToSendProps_R( PropLookup, pDecoder->GetSendTable()->m_pNetTableName, pDecoder->GetSendTable(), pDecoder->GetRecvTable(), bAllowMismatches, pAnyMismatches ) )
  498. {
  499. bRet = false;
  500. }
  501. else
  502. {
  503. // Now fill out the matching RecvProp array.
  504. CSendTablePrecalc *pPrecalc = &pDecoder->m_Precalc;
  505. CopySendPropsToRecvProps( PropLookup, pPrecalc->m_Props, pDecoder->m_Props );
  506. CopySendPropsToRecvProps( PropLookup, pPrecalc->m_DatatableProps, pDecoder->m_DatatableProps );
  507. DTI_HookRecvDecoder( pDecoder );
  508. }
  509. }
  510. return bRet;
  511. }
  512. bool RecvTable_Decode(
  513. RecvTable *pTable,
  514. void *pStruct,
  515. SerializedEntityHandle_t dest,
  516. int objectID
  517. )
  518. {
  519. CRecvDecoder *pDecoder = pTable->m_pDecoder;
  520. ErrorIfNot( pDecoder,
  521. ("RecvTable_Decode: table '%s' missing a decoder.", pTable->GetName())
  522. );
  523. CSerializedEntity *pEntity = reinterpret_cast< CSerializedEntity * >( dest );
  524. Assert( pEntity );
  525. // While there are properties, decode them.. walk the stack as you go.
  526. CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID );
  527. theStack.Init( false, false );
  528. bf_read buf;
  529. buf.SetDebugName( "CFlattenedSerializer::Decode" );
  530. pEntity->StartReading( buf );
  531. CFieldPath path;
  532. int nDataOffset;
  533. int nNextDataOffset;
  534. for ( int nFieldIndex = 0 ; nFieldIndex < pEntity->GetFieldCount() ; ++nFieldIndex )
  535. {
  536. pEntity->GetField( nFieldIndex, path, &nDataOffset, &nNextDataOffset );
  537. buf.Seek( nDataOffset );
  538. theStack.SeekToProp( path );
  539. const RecvProp *pProp = pDecoder->GetProp( path );
  540. DecodeInfo decodeInfo;
  541. decodeInfo.m_pStruct = theStack.GetCurStructBase();
  542. if ( pProp )
  543. {
  544. decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset();
  545. }
  546. else
  547. {
  548. // They're allowed to be missing props here if they're playing back a demo.
  549. // This allows us to change the datatables and still preserve old demos.
  550. decodeInfo.m_pData = NULL;
  551. }
  552. decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed.
  553. decodeInfo.m_pProp = pDecoder->GetSendProp( path );
  554. decodeInfo.m_pIn = &buf;
  555. decodeInfo.m_ObjectID = objectID;
  556. g_PropTypeFns[ decodeInfo.m_pProp->GetType() ].Decode( &decodeInfo );
  557. }
  558. return !buf.IsOverflowed();
  559. }
  560. void RecvTable_DecodeZeros( RecvTable *pTable, void *pStruct, int objectID )
  561. {
  562. CRecvDecoder *pDecoder = pTable->m_pDecoder;
  563. ErrorIfNot( pDecoder,
  564. ("RecvTable_DecodeZeros: table '%s' missing a decoder.", pTable->GetName())
  565. );
  566. // While there are properties, decode them.. walk the stack as you go.
  567. CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID );
  568. theStack.Init( false, false );
  569. for ( int iProp=0; iProp < pDecoder->GetNumProps(); iProp++ )
  570. {
  571. theStack.SeekToProp( iProp );
  572. const RecvProp *pProp = pDecoder->GetProp( iProp );
  573. DecodeInfo decodeInfo;
  574. decodeInfo.m_pStruct = theStack.GetCurStructBase();
  575. decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset();
  576. decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed.
  577. decodeInfo.m_pProp = pDecoder->GetSendProp( iProp );
  578. decodeInfo.m_pIn = NULL;
  579. decodeInfo.m_ObjectID = objectID;
  580. g_PropTypeFns[pProp->GetType()].DecodeZero( &decodeInfo );
  581. }
  582. }
  583. // Copies pProp's state from pIn to pOut. pDecodeInfo MUST be setup by calling InitDecodeInfoForSkippingProps
  584. // with pIn.
  585. //
  586. // NOTE: this routine isn't optimal. If it shows up on the profiles, then it's easy to
  587. // make this fast by adding a special routine to copy a property's state to PropTypeFns.
  588. static void CopyPropState(
  589. CRecvDecoder *pDecoder,
  590. int iSendProp,
  591. bf_read *pIn,
  592. CDeltaBitsWriter *pOut
  593. )
  594. {
  595. const SendProp *pProp = pDecoder->GetSendProp( iSendProp );
  596. int iStartBit = pIn->GetNumBitsRead();
  597. // skip over data
  598. SkipPropData( pIn, pProp );
  599. // Figure out how many bits it took.
  600. int nBits = pIn->GetNumBitsRead() - iStartBit;
  601. // Copy the data
  602. pIn->Seek( iStartBit );
  603. pOut->WritePropIndex( iSendProp );
  604. pOut->GetBitBuf()->WriteBitsFromBuffer( pIn, nBits );
  605. }
  606. bool RecvTable_MergeDeltas(
  607. RecvTable *pTable,
  608. SerializedEntityHandle_t oldState, // Can be invalid
  609. SerializedEntityHandle_t newState,
  610. SerializedEntityHandle_t mergedState,
  611. int objectID,
  612. CUtlVector< int > *pVecChanges
  613. )
  614. {
  615. Assert( SERIALIZED_ENTITY_HANDLE_INVALID != newState );
  616. Assert( SERIALIZED_ENTITY_HANDLE_INVALID != mergedState );
  617. CSerializedEntity *pOldState = oldState != SERIALIZED_ENTITY_HANDLE_INVALID ? reinterpret_cast< CSerializedEntity * >( oldState ) : NULL;
  618. CSerializedEntity *pNewState = reinterpret_cast< CSerializedEntity * >( newState );
  619. Assert( pNewState );
  620. CSerializedEntity *pMergedState = reinterpret_cast< CSerializedEntity * >( mergedState );
  621. Assert( pMergedState );
  622. ErrorIfNot( pTable && pNewState && pMergedState, ("RecvTable_MergeDeltas: invalid parameters passed.")
  623. );
  624. CRecvDecoder *pDecoder = pTable->m_pDecoder;
  625. ErrorIfNot( pDecoder, ("RecvTable_MergeDeltas: table '%s' is missing its decoder.", pTable->GetName()) );
  626. CSerializedEntityFieldIterator oldIterator( pOldState );
  627. CSerializedEntityFieldIterator newIterator( pNewState );
  628. bf_read oldBits, newBits;
  629. oldBits.SetDebugName( "CFlattenedSerializer::MergeDeltas: oldBits" );
  630. newBits.SetDebugName( "CFlattenedSerializer::MergeDeltas: newBits" );
  631. if ( pOldState )
  632. {
  633. pOldState->StartReading( oldBits );
  634. }
  635. pNewState->StartReading( newBits );
  636. pMergedState->Clear();
  637. const CFieldPath *oldFieldPath = oldIterator.FirstPtr();
  638. const CFieldPath *newFieldPath = newIterator.FirstPtr();
  639. uint8 packedData[MAX_PACKEDENTITY_DATA];
  640. bf_write fieldDataBuf( "CFlattenedSerializer::WriteFieldList fieldDataBuf", packedData, sizeof( packedData ) );
  641. while ( 1 )
  642. {
  643. // Write any properties in the previous state that aren't in the new state.
  644. while ( *oldFieldPath < *newFieldPath )
  645. {
  646. pMergedState->AddPathAndOffset( *oldFieldPath, fieldDataBuf.GetNumBitsWritten() );
  647. oldBits.Seek( oldIterator.GetOffset() );
  648. fieldDataBuf.WriteBitsFromBuffer( &oldBits, oldIterator.GetLength() );
  649. oldFieldPath = oldIterator.NextPtr();
  650. }
  651. if ( *newFieldPath == PROP_SENTINEL )
  652. break;
  653. // Check if we're at the end here so the while() statement above can seek the old buffer
  654. // to its end too.
  655. bool bBoth = ( *oldFieldPath == *newFieldPath );
  656. // If the old state has this property too, then just skip over its data.
  657. if ( bBoth )
  658. {
  659. oldFieldPath = oldIterator.NextPtr();
  660. }
  661. pMergedState->AddPathAndOffset( *newFieldPath, fieldDataBuf.GetNumBitsWritten() );
  662. newBits.Seek( newIterator.GetOffset() );
  663. fieldDataBuf.WriteBitsFromBuffer( &newBits, newIterator.GetLength() );
  664. if ( pVecChanges )
  665. {
  666. pVecChanges->AddToTail( *newFieldPath );
  667. }
  668. newFieldPath = newIterator.NextPtr();
  669. }
  670. pMergedState->PackWithFieldData( packedData, fieldDataBuf.GetNumBitsWritten() );
  671. ErrorIfNot( !fieldDataBuf.IsOverflowed(), ("RecvTable_MergeDeltas: overflowed in RecvTable '%s'.", pTable->GetName() ) );
  672. return true;
  673. }
  674. template< bool bDTIEnabled >
  675. bool RecvTable_ReadFieldList_Guts(
  676. RecvTable *pTable,
  677. bf_read &buf,
  678. SerializedEntityHandle_t dest,
  679. int nObjectId
  680. )
  681. {
  682. CSerializedEntity *pEntity = reinterpret_cast< CSerializedEntity * >( dest );
  683. Assert( pEntity );
  684. pEntity->Clear();
  685. CUtlVector< int > fieldBits;
  686. pEntity->ReadFieldPaths( &buf, bDTIEnabled ? &fieldBits : NULL );
  687. ErrorIfNot( pTable, ("RecvTable_ReadFieldListt: Missing RecvTable for class\n" ) );
  688. if ( !pTable )
  689. return false;
  690. CRecvDecoder *pDecoder = pTable->m_pDecoder;
  691. ErrorIfNot( pDecoder, ("RecvTable_ReadFieldList: table '%s' missing a decoder.", pTable->GetName()) );
  692. // Remember where the "data" payload started
  693. int nStartBit = buf.GetNumBitsRead();
  694. CFieldPath path;
  695. for ( int nFieldIndex = 0; nFieldIndex < pEntity->GetFieldCount(); ++nFieldIndex )
  696. {
  697. int nDataOffset = buf.GetNumBitsRead();
  698. path = pEntity->GetFieldPath( nFieldIndex );
  699. pEntity->SetFieldDataBitOffset( nFieldIndex, nDataOffset - nStartBit ); // Offset from start of data payload
  700. const SendProp *pSendProp = pDecoder->GetSendProp( path );
  701. g_PropTypeFns[ pSendProp->GetType() ].SkipProp( pSendProp, &buf );
  702. // buffer now just after payload
  703. if ( bDTIEnabled )
  704. {
  705. DTI_HookDeltaBits( pDecoder, path, buf.GetNumBitsRead() - nDataOffset, fieldBits[ nFieldIndex ] );
  706. }
  707. }
  708. int nLastBit = buf.GetNumBitsRead();
  709. // Rewind
  710. buf.Seek( nStartBit );
  711. // Copy
  712. pEntity->PackWithFieldData( buf, nLastBit - nStartBit );
  713. // Put head back to end
  714. buf.Seek( nLastBit );
  715. return true;
  716. }
  717. bool RecvTable_ReadFieldList(
  718. RecvTable *pTable,
  719. bf_read &buf,
  720. SerializedEntityHandle_t dest,
  721. int nObjectId,
  722. bool bUpdateDTI
  723. )
  724. {
  725. if ( g_bDTIEnabled && bUpdateDTI )
  726. {
  727. return RecvTable_ReadFieldList_Guts< true >( pTable, buf, dest, nObjectId );
  728. }
  729. return RecvTable_ReadFieldList_Guts< false >( pTable, buf, dest, nObjectId );
  730. }