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.

617 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "server.h"
  8. #include "dt_stack.h"
  9. #include "dt_localtransfer.h"
  10. #include "mathlib/vector.h"
  11. #include "edict.h"
  12. #include "convar.h"
  13. #include "con_nprint.h"
  14. #include "utldict.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. #define PROP_INDEX_VECTOR_ELEM_MARKER 0x8000
  18. static ConVar dt_UsePartialChangeEnts(
  19. "dt_UsePartialChangeEnts",
  20. "1",
  21. 0,
  22. "(SP only) - enable FL_EDICT_PARTIAL_CHANGE optimization."
  23. );
  24. static ConVar dt_ShowPartialChangeEnts(
  25. "dt_ShowPartialChangeEnts",
  26. "0",
  27. 0,
  28. "(SP only) - show entities that were copied using small optimized lists (FL_EDICT_PARTIAL_CHANGE)."
  29. );
  30. // Negative numbers represent entities that were fully copied.
  31. static CUtlVector<int> g_PartialChangeEnts;
  32. static int g_nTotalPropChanges = 0;
  33. static int g_nTotalEntChanges = 0;
  34. template<class T>
  35. inline void LocalTransfer_FastType(
  36. T *pBlah,
  37. CServerDatatableStack &serverStack,
  38. CClientDatatableStack &clientStack,
  39. CFastLocalTransferPropInfo *pPropList,
  40. int nProps )
  41. {
  42. CFastLocalTransferPropInfo *pCur = pPropList;
  43. for ( int i=0; i < nProps; i++, pCur++ )
  44. {
  45. serverStack.SeekToProp( pCur->m_iProp );
  46. unsigned char *pServerBase = serverStack.GetCurStructBase();
  47. if ( pServerBase )
  48. {
  49. clientStack.SeekToProp( pCur->m_iProp );
  50. unsigned char *pClientBase = clientStack.GetCurStructBase();
  51. Assert( pClientBase );
  52. const T *pSource = (const T*)( pServerBase + pCur->m_iSendOffset );
  53. T *pDest = (T*)( pClientBase + pCur->m_iRecvOffset );
  54. *pDest = *pSource;
  55. }
  56. }
  57. }
  58. void AddPropOffsetToMap( CSendTablePrecalc *pPrecalc, int iInProp, int iInOffset )
  59. {
  60. Assert( iInProp < 0xFFFF && iInOffset < 0xFFFF );
  61. unsigned short iProp = (unsigned short)iInProp;
  62. unsigned short iOffset = (unsigned short)iInOffset;
  63. unsigned short iOldIndex = pPrecalc->m_PropOffsetToIndexMap.Find( iOffset );
  64. if ( iOldIndex != pPrecalc->m_PropOffsetToIndexMap.InvalidIndex() )
  65. {
  66. return;
  67. }
  68. pPrecalc->m_PropOffsetToIndexMap.Insert( iOffset, iProp );
  69. }
  70. // This helps us figure out which properties can use the super-optimized mode
  71. // where they are tracked in a list when they change. If their m_pProxies pointers
  72. // are set to 1, then it means that this property is gotten to by means of SendProxy_DataTableToDataTable.
  73. // If it's set to 0, then we can't directly take the property's offset.
  74. class CPropMapStack : public CDatatableStack
  75. {
  76. public:
  77. CPropMapStack( CSendTablePrecalc *pPrecalc, const CStandardSendProxies *pSendProxies ) :
  78. CDatatableStack( pPrecalc, (unsigned char*)1, -1 )
  79. {
  80. m_pPropMapStackPrecalc = pPrecalc;
  81. m_pSendProxies = pSendProxies;
  82. }
  83. bool IsNonPointerModifyingProxy( SendTableProxyFn fn, const CStandardSendProxies *pSendProxies )
  84. {
  85. if ( fn == m_pSendProxies->m_DataTableToDataTable ||
  86. fn == m_pSendProxies->m_SendLocalDataTable )
  87. {
  88. return true;
  89. }
  90. if( pSendProxies->m_ppNonModifiedPointerProxies )
  91. {
  92. CNonModifiedPointerProxy *pCur = *pSendProxies->m_ppNonModifiedPointerProxies;
  93. while ( pCur )
  94. {
  95. if ( pCur->m_Fn == fn )
  96. return true;
  97. pCur = pCur->m_pNext;
  98. }
  99. }
  100. return false;
  101. }
  102. inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
  103. {
  104. if ( !pStructBase )
  105. return 0;
  106. const SendProp *pProp = m_pPropMapStackPrecalc->GetDatatableProp( iProp );
  107. if ( IsNonPointerModifyingProxy( pProp->GetDataTableProxyFn(), m_pSendProxies ) )
  108. {
  109. // Note: these are offset by 1 (see the constructor), otherwise it won't recurse
  110. // during the Init call because pCurStructBase is 0.
  111. return pStructBase + pProp->GetOffset();
  112. }
  113. else
  114. {
  115. return 0;
  116. }
  117. }
  118. virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
  119. {
  120. // Remember where the game code pointed us for this datatable's data so
  121. m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
  122. for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
  123. {
  124. CSendNode *pCurChild = pNode->GetChild( iChild );
  125. unsigned char *pNewStructBase = NULL;
  126. if ( pStructBase )
  127. {
  128. pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
  129. }
  130. RecurseAndCallProxies( pCurChild, pNewStructBase );
  131. }
  132. }
  133. public:
  134. CSendTablePrecalc *m_pPropMapStackPrecalc;
  135. const CStandardSendProxies *m_pSendProxies;
  136. };
  137. void BuildPropOffsetToIndexMap( CSendTablePrecalc *pPrecalc, const CStandardSendProxies *pSendProxies )
  138. {
  139. CPropMapStack pmStack( pPrecalc, pSendProxies );
  140. pmStack.Init();
  141. for ( int i=0; i < pPrecalc->m_Props.Count(); i++ )
  142. {
  143. pmStack.SeekToProp( i );
  144. if ( pmStack.GetCurStructBase() != 0 )
  145. {
  146. const SendProp *pProp = pPrecalc->m_Props[i];
  147. int offset = pProp->GetOffset() + (int)pmStack.GetCurStructBase() - 1;
  148. int elementCount = 1;
  149. int elementStride = 0;
  150. if ( pProp->GetType() == DPT_Array )
  151. {
  152. offset = pProp->GetArrayProp()->GetOffset() + (int)pmStack.GetCurStructBase() - 1;
  153. elementCount = pProp->m_nElements;
  154. elementStride = pProp->m_ElementStride;
  155. }
  156. if ( offset != 0 )
  157. {
  158. for ( int j = 0; j < elementCount; j++ )
  159. {
  160. if ( pProp->GetFlags() & SPROP_IS_A_VECTOR_ELEM )
  161. {
  162. AddPropOffsetToMap( pPrecalc, i | PROP_INDEX_VECTOR_ELEM_MARKER, offset );
  163. }
  164. else
  165. {
  166. AddPropOffsetToMap( pPrecalc, i, offset );
  167. }
  168. offset += elementStride;
  169. }
  170. }
  171. }
  172. }
  173. }
  174. void LocalTransfer_InitFastCopy(
  175. const SendTable *pSendTable,
  176. const CStandardSendProxies *pSendProxies,
  177. RecvTable *pRecvTable,
  178. const CStandardRecvProxies *pRecvProxies,
  179. int &nSlowCopyProps, // These are incremented to tell you how many fast copy props it found.
  180. int &nFastCopyProps
  181. )
  182. {
  183. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  184. // Setup the offset-to-index map.
  185. pPrecalc->m_PropOffsetToIndexMap.RemoveAll();
  186. BuildPropOffsetToIndexMap( pPrecalc, pSendProxies );
  187. // Clear the old lists.
  188. pPrecalc->m_FastLocalTransfer.m_FastInt32.Purge();
  189. pPrecalc->m_FastLocalTransfer.m_FastInt16.Purge();
  190. pPrecalc->m_FastLocalTransfer.m_FastInt8.Purge();
  191. pPrecalc->m_FastLocalTransfer.m_FastVector.Purge();
  192. pPrecalc->m_FastLocalTransfer.m_OtherProps.Purge();
  193. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  194. int iNumProp = pPrecalc->GetNumProps();
  195. for ( int iProp=0; iProp < iNumProp; iProp++ )
  196. {
  197. const SendProp *pSendProp = pPrecalc->GetProp( iProp );
  198. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  199. if ( pRecvProp )
  200. {
  201. Assert( stricmp( pSendProp->GetName(), pRecvProp->GetName() ) == 0 );
  202. CUtlVector<CFastLocalTransferPropInfo> *pList = &pPrecalc->m_FastLocalTransfer.m_OtherProps;
  203. if ( pSendProp->GetType() == DPT_Int &&
  204. (pSendProp->GetProxyFn() == pSendProxies->m_Int32ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt32ToInt32) &&
  205. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt32 )
  206. {
  207. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt32;
  208. ++nFastCopyProps;
  209. }
  210. else if( pSendProp->GetType() == DPT_Int &&
  211. (pSendProp->GetProxyFn() == pSendProxies->m_Int16ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt16ToInt32) &&
  212. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt16 )
  213. {
  214. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt16;
  215. ++nFastCopyProps;
  216. }
  217. else if( pSendProp->GetType() == DPT_Int &&
  218. (pSendProp->GetProxyFn() == pSendProxies->m_Int8ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt8ToInt32) &&
  219. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt8 )
  220. {
  221. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt8;
  222. ++nFastCopyProps;
  223. }
  224. else if( pSendProp->GetType() == DPT_Float &&
  225. pSendProp->GetProxyFn() == pSendProxies->m_FloatToFloat &&
  226. pRecvProp->GetProxyFn() == pRecvProxies->m_FloatToFloat )
  227. {
  228. Assert( sizeof( int ) == sizeof( float ) );
  229. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt32;
  230. ++nFastCopyProps;
  231. }
  232. else if ( pSendProp->GetType() == DPT_Vector &&
  233. pSendProp->GetProxyFn() == pSendProxies->m_VectorToVector &&
  234. pRecvProp->GetProxyFn() == pRecvProxies->m_VectorToVector )
  235. {
  236. pList = &pPrecalc->m_FastLocalTransfer.m_FastVector;
  237. ++nFastCopyProps;
  238. }
  239. else
  240. {
  241. ++nSlowCopyProps;
  242. }
  243. CFastLocalTransferPropInfo toAdd;
  244. toAdd.m_iProp = iProp;
  245. toAdd.m_iRecvOffset = pRecvProp->GetOffset();
  246. toAdd.m_iSendOffset = pSendProp->GetOffset();
  247. pList->AddToTail( toAdd );
  248. }
  249. }
  250. }
  251. inline int MapPropOffsetsToIndices(
  252. const CBaseEdict *pEdict,
  253. CSendTablePrecalc *pPrecalc,
  254. const unsigned short *pOffsets,
  255. unsigned short nOffsets,
  256. unsigned short *pOut )
  257. {
  258. int iOut = 0;
  259. for ( unsigned short i=0; i < nOffsets; i++ )
  260. {
  261. unsigned short index = pPrecalc->m_PropOffsetToIndexMap.Find( pOffsets[i] );
  262. if ( index == pPrecalc->m_PropOffsetToIndexMap.InvalidIndex() )
  263. {
  264. // Note: this SHOULD be fine. In all known cases, when NetworkStateChanged is called with
  265. // an offset, there should be a corresponding SendProp in order for that NetworkStateChanged
  266. // call to mean anything.
  267. //
  268. // It means that if we can't find an offset here, then there isn't a SendProp
  269. // associated with the CNetworkVar that triggered the change. Therefore,
  270. // the change doesn't matter and we can skip past it.
  271. //
  272. // If we wanted to be anal, we could force them to use DISABLE_NETWORK_VAR_FOR_DERIVED
  273. // appropriately in all these cases, but then we'd need a ton of them for certain classes
  274. // (like CBaseViewModel, which has a slew of CNetworkVars in its base classes that
  275. // it doesn't want to transmit).
  276. if ( dt_ShowPartialChangeEnts.GetInt() )
  277. {
  278. static CUtlDict<int,int> testDict;
  279. char str[512];
  280. Q_snprintf( str, sizeof( str ), "LocalTransfer offset miss - class: %s, DT: %s, offset: %d", pEdict->GetClassName(), pPrecalc->m_pSendTable->m_pNetTableName, pOffsets[i] );
  281. if ( testDict.Find( str ) == testDict.InvalidIndex() )
  282. {
  283. testDict.Insert( str );
  284. Warning( "%s\n", str );
  285. }
  286. }
  287. }
  288. else
  289. {
  290. unsigned short propIndex = pPrecalc->m_PropOffsetToIndexMap[index];
  291. if ( propIndex & PROP_INDEX_VECTOR_ELEM_MARKER )
  292. {
  293. // Look for all 3 vector elems here.
  294. unsigned short curOffset = pOffsets[i];
  295. for ( int iVectorElem=0; iVectorElem < 3; iVectorElem++ )
  296. {
  297. index = pPrecalc->m_PropOffsetToIndexMap.Find( curOffset );
  298. if ( index == 0xFFFF )
  299. {
  300. break;
  301. }
  302. else
  303. {
  304. propIndex = pPrecalc->m_PropOffsetToIndexMap[index];
  305. if ( propIndex & PROP_INDEX_VECTOR_ELEM_MARKER )
  306. pOut[iOut++] = (propIndex & ~PROP_INDEX_VECTOR_ELEM_MARKER);
  307. }
  308. curOffset += sizeof( float );
  309. }
  310. }
  311. else
  312. {
  313. pOut[iOut++] = propIndex;
  314. }
  315. }
  316. }
  317. return iOut;
  318. }
  319. inline void FastSortList( unsigned short *pList, unsigned short nEntries )
  320. {
  321. if ( nEntries == 1 )
  322. return;
  323. unsigned short i = 0;
  324. while ( 1 )
  325. {
  326. if ( pList[i+1] < pList[i] )
  327. {
  328. unsigned short tmp = pList[i+1];
  329. pList[i+1] = pList[i];
  330. pList[i] = tmp;
  331. if ( i > 0 )
  332. --i;
  333. }
  334. else
  335. {
  336. ++i;
  337. if ( i >= (nEntries-1) )
  338. return;
  339. }
  340. }
  341. }
  342. inline void AddToPartialChangeEntsList( int iEnt, bool bPartial )
  343. {
  344. if ( !dt_ShowPartialChangeEnts.GetInt() )
  345. return;
  346. #if !defined( _XBOX )
  347. if ( !bPartial )
  348. iEnt = -iEnt;
  349. if ( g_PartialChangeEnts.Find( iEnt ) == -1 )
  350. g_PartialChangeEnts.AddToTail( iEnt );
  351. #endif
  352. }
  353. void PrintPartialChangeEntsList()
  354. {
  355. if ( !dt_ShowPartialChangeEnts.GetInt() )
  356. return;
  357. #if !defined( _XBOX )
  358. int iCurRow = 15;
  359. Con_NPrintf( iCurRow++, "----- dt_ShowPartialChangeEnts -----" );
  360. Con_NPrintf( iCurRow++, "" );
  361. Con_NPrintf( iCurRow++, "Ent changes: %3d, prop changes: %3d",
  362. g_nTotalEntChanges, g_nTotalPropChanges );
  363. Con_NPrintf( iCurRow++, "" );
  364. char str[512];
  365. bool bFirst = true;
  366. // Write the partial ents.
  367. str[0] = 0;
  368. for ( int i=0; i < g_PartialChangeEnts.Count(); i++ )
  369. {
  370. if ( g_PartialChangeEnts[i] >= 0 )
  371. {
  372. if ( !bFirst )
  373. Q_strncat( str, ", ", sizeof( str ) );
  374. char tempStr[512];
  375. Q_snprintf( tempStr, sizeof( tempStr ), "%d", g_PartialChangeEnts[i] );
  376. Q_strncat( str, tempStr, sizeof( str ) );
  377. bFirst = false;
  378. }
  379. }
  380. Q_strncat( str, " - PARTIAL", sizeof( str ) );
  381. Con_NPrintf( iCurRow++, "%s", str );
  382. // Write the full ents.
  383. bFirst = true;
  384. str[0] = 0;
  385. for ( int i=0; i < g_PartialChangeEnts.Count(); i++ )
  386. {
  387. if ( g_PartialChangeEnts[i] < 0 )
  388. {
  389. if ( !bFirst )
  390. Q_strncat( str, ", ", sizeof( str ) );
  391. char tempStr[512];
  392. Q_snprintf( tempStr, sizeof( tempStr ), "%d", -g_PartialChangeEnts[i] );
  393. Q_strncat( str, tempStr, sizeof( str ) );
  394. bFirst = false;
  395. }
  396. }
  397. Q_strncat( str, " - FULL", sizeof( str ) );
  398. Con_NPrintf( iCurRow++, "%s", str );
  399. g_PartialChangeEnts.Purge();
  400. g_nTotalPropChanges = g_nTotalEntChanges = 0;
  401. #endif
  402. }
  403. void LocalTransfer_TransferEntity(
  404. const CBaseEdict *pEdict,
  405. const SendTable *pSendTable,
  406. const void *pSrcEnt,
  407. RecvTable *pRecvTable,
  408. void *pDestEnt,
  409. bool bNewlyCreated,
  410. bool bJustEnteredPVS,
  411. int objectID )
  412. {
  413. ++g_nTotalEntChanges;
  414. CEdictChangeInfo *pCI = &g_pSharedChangeInfo->m_ChangeInfos[pEdict->GetChangeInfo()];
  415. unsigned short propIndices[MAX_CHANGE_OFFSETS*3];
  416. // This code tries to only copy fields expressly marked as "changed" (by having the field offsets added to the changeoffsets vectors)
  417. if ( pEdict->GetChangeInfoSerialNumber() == g_pSharedChangeInfo->m_iSerialNumber &&
  418. !bNewlyCreated &&
  419. !bJustEnteredPVS &&
  420. dt_UsePartialChangeEnts.GetInt()
  421. )
  422. {
  423. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  424. int nChangeOffsets = MapPropOffsetsToIndices( pEdict, pPrecalc, pCI->m_ChangeOffsets, pCI->m_nChangeOffsets, propIndices );
  425. if ( nChangeOffsets == 0 )
  426. return;
  427. AddToPartialChangeEntsList( (edict_t*)pEdict - sv.edicts, true );
  428. FastSortList( propIndices, nChangeOffsets );
  429. // Setup the structure to traverse the source tree.
  430. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pSendTable->m_pNetTableName) );
  431. CServerDatatableStack serverStack( pPrecalc, (unsigned char*)pSrcEnt, objectID );
  432. serverStack.Init( true );
  433. // Setup the structure to traverse the dest tree.
  434. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  435. ErrorIfNot( pDecoder, ("RecvTable_Decode: table '%s' missing a decoder.", pRecvTable->GetName()) );
  436. CClientDatatableStack clientStack( pDecoder, (unsigned char*)pDestEnt, objectID );
  437. clientStack.Init( true );
  438. // Cool. We can get away with just transferring a few.
  439. for ( int iChanged=0; iChanged < nChangeOffsets; iChanged++ )
  440. {
  441. int iProp = propIndices[iChanged];
  442. ++g_nTotalPropChanges;
  443. serverStack.SeekToProp( iProp );
  444. const SendProp *pSendProp = serverStack.GetCurProp();
  445. unsigned char *pSendBase = serverStack.UpdateRoutesExplicit();
  446. if ( pSendBase )
  447. {
  448. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  449. Assert( pRecvProp );
  450. clientStack.SeekToProp( iProp );
  451. unsigned char *pRecvBase = clientStack.UpdateRoutesExplicit();
  452. Assert( pRecvBase );
  453. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  454. }
  455. }
  456. }
  457. // Whereas the below code copies _all_ fields, regardless of whether they were changed or not. We run this only newly created entities, or entities
  458. // which were previously dormant/outside the pvs but are now back in the PVS since we could have missed field updates since the changeoffsets get cleared every
  459. // frame.
  460. else
  461. {
  462. // Setup the structure to traverse the source tree.
  463. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  464. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pSendTable->m_pNetTableName) );
  465. CServerDatatableStack serverStack( pPrecalc, (unsigned char*)pSrcEnt, objectID );
  466. serverStack.Init();
  467. // Setup the structure to traverse the dest tree.
  468. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  469. ErrorIfNot( pDecoder, ("RecvTable_Decode: table '%s' missing a decoder.", pRecvTable->GetName()) );
  470. CClientDatatableStack clientStack( pDecoder, (unsigned char*)pDestEnt, objectID );
  471. clientStack.Init();
  472. AddToPartialChangeEntsList( (edict_t*)pEdict - sv.edicts, false );
  473. // Copy the properties that require proxies.
  474. CFastLocalTransferPropInfo *pPropList = pPrecalc->m_FastLocalTransfer.m_OtherProps.Base();
  475. int nProps = pPrecalc->m_FastLocalTransfer.m_OtherProps.Count();
  476. for ( int i=0; i < nProps; i++ )
  477. {
  478. int iProp = pPropList[i].m_iProp;
  479. serverStack.SeekToProp( iProp );
  480. const SendProp *pSendProp = serverStack.GetCurProp();
  481. unsigned char *pSendBase = serverStack.GetCurStructBase();
  482. if ( pSendBase )
  483. {
  484. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  485. Assert( pRecvProp );
  486. clientStack.SeekToProp( iProp );
  487. unsigned char *pRecvBase = clientStack.GetCurStructBase();
  488. Assert( pRecvBase );
  489. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  490. }
  491. }
  492. // Transfer over the fast properties.
  493. LocalTransfer_FastType( (int*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt32.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt32.Count() );
  494. LocalTransfer_FastType( (short*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt16.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt16.Count() );
  495. LocalTransfer_FastType( (char*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt8.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt8.Count() );
  496. LocalTransfer_FastType( (Vector*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastVector.Base(), pPrecalc->m_FastLocalTransfer.m_FastVector.Count() );
  497. }
  498. // The old, slow method to copy all props using their proxies.
  499. /*
  500. int iEndProp = pPrecalc->m_Root.GetLastPropIndex();
  501. for ( int iProp=0; iProp <= iEndProp; iProp++ )
  502. {
  503. serverStack.SeekToProp( iProp );
  504. clientStack.SeekToProp( iProp );
  505. const SendProp *pSendProp = serverStack.GetCurProp();
  506. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  507. if ( pRecvProp )
  508. {
  509. unsigned char *pSendBase = serverStack.GetCurStructBase();
  510. unsigned char *pRecvBase = clientStack.GetCurStructBase();
  511. if ( pSendBase && pRecvBase )
  512. {
  513. Assert( stricmp( pSendProp->GetName(), pRecvProp->GetName() ) == 0 );
  514. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  515. }
  516. }
  517. }
  518. */
  519. }