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.

707 lines
22 KiB

  1. //========= Copyright � 1996-2005, 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. #include "dt_localtransfer.h"
  16. #include "networkvar.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. static ConVar dt_UsePartialChangeEnts(
  20. "dt_UsePartialChangeEnts",
  21. "1",
  22. 0,
  23. "(SP only) - enable FL_EDICT_PARTIAL_CHANGE optimization."
  24. );
  25. static ConVar dt_ShowPartialChangeEnts(
  26. "dt_ShowPartialChangeEnts",
  27. "0",
  28. 0,
  29. "(SP only) - show entities that were copied using small optimized lists (FL_EDICT_PARTIAL_CHANGE)."
  30. );
  31. // Negative numbers represent entities that were fully copied.
  32. static CUtlVector<int> g_PartialChangeEnts;
  33. static int g_nTotalPropChanges = 0;
  34. static int g_nTotalEntChanges = 0;
  35. template<class T>
  36. inline void LocalTransfer_FastType(
  37. T *pBlah,
  38. CServerDatatableStack &serverStack,
  39. CClientDatatableStack &clientStack,
  40. CFastLocalTransferPropInfo *pPropList,
  41. int nProps )
  42. {
  43. CFastLocalTransferPropInfo *pCur = pPropList;
  44. for ( int i=0; i < nProps; i++, pCur++ )
  45. {
  46. serverStack.SeekToProp( pCur->m_iProp );
  47. unsigned char *pServerBase = serverStack.GetCurStructBase();
  48. if ( pServerBase )
  49. {
  50. clientStack.SeekToProp( pCur->m_iProp );
  51. unsigned char *pClientBase = clientStack.GetCurStructBase();
  52. Assert( pClientBase );
  53. const T *pSource = (const T*)( pServerBase + pCur->m_iSendOffset );
  54. T *pDest = (T*)( pClientBase + pCur->m_iRecvOffset );
  55. *pDest = *pSource;
  56. }
  57. }
  58. }
  59. void AddPropOffsetToMap( CSendTablePrecalc *pPrecalc, int iInProp, int iInOffset, const char *pszPropName )
  60. {
  61. Assert( iInProp < 0xFFFF && iInOffset < 0xFFFF );
  62. unsigned short iProp = (unsigned short)iInProp;
  63. unsigned short iOffset = (unsigned short)iInOffset;
  64. unsigned short iOldIndex = pPrecalc->m_PropOffsetToIndexMap.Find( iOffset );
  65. if ( iOldIndex == pPrecalc->m_PropOffsetToIndexMap.InvalidIndex() )
  66. {
  67. PropIndicesCollection_t coll;
  68. coll.m_Indices[0] = iProp;
  69. for ( int i=1; i < ARRAYSIZE( coll.m_Indices ); i++ )
  70. {
  71. coll.m_Indices[i] = 0xFFFF;
  72. }
  73. pPrecalc->m_PropOffsetToIndexMap.Insert( iOffset, coll );
  74. }
  75. else
  76. {
  77. // At least one SendProp is pointing at this offset. No problem. We'll
  78. // remember all of them and trigger a change in all the SendProps attached
  79. // to this offset.
  80. //
  81. // Look for a slot in the PropIndicesCollection_t to hold the prop index.
  82. PropIndicesCollection_t &coll = pPrecalc->m_PropOffsetToIndexMap[iOldIndex];
  83. int i;
  84. for ( i=0; i < ARRAYSIZE( coll.m_Indices ); i++ )
  85. {
  86. if ( coll.m_Indices[i] == 0xFFFF )
  87. {
  88. coll.m_Indices[i] = iProp;
  89. break;
  90. }
  91. }
  92. if ( i >= ARRAYSIZE( coll.m_Indices ) )
  93. {
  94. // If there wasn't enough space to hold this SendProp index, then we have a CNetworkVar that won't
  95. // cause this SendProp to get sent. The fixes are to increase the length of PropIndicesCollection_t::m_Indices
  96. // or to have less references to this SendProp in the class' tree.
  97. Error( "Overflowed a PropIndicesCollection_t on %s\n", pPrecalc->m_pSendTable->GetName() );
  98. }
  99. }
  100. }
  101. // This helps us figure out which properties can use the super-optimized mode
  102. // where they are tracked in a list when they change. If their m_pProxies pointers
  103. // are set to 1, then it means that this property is gotten to by means of SendProxy_DataTableToDataTable.
  104. // If it's set to 0, then we can't directly take the property's offset.
  105. class CPropMapStack : public CDatatableStack
  106. {
  107. public:
  108. CPropMapStack( CSendTablePrecalc *pPrecalc, const CStandardSendProxies *pSendProxies ) :
  109. CDatatableStack( pPrecalc, (unsigned char*)1, -1 )
  110. {
  111. m_pPropMapStackPrecalc = pPrecalc;
  112. m_pSendProxies = pSendProxies;
  113. }
  114. bool IsNonPointerModifyingProxy( SendTableProxyFn fn, const CStandardSendProxies *pSendProxies )
  115. {
  116. if ( fn == m_pSendProxies->m_DataTableToDataTable )
  117. {
  118. return true;
  119. }
  120. if( pSendProxies->m_ppNonModifiedPointerProxies )
  121. {
  122. CNonModifiedPointerProxy *pCur = *pSendProxies->m_ppNonModifiedPointerProxies;
  123. while ( pCur )
  124. {
  125. if ( pCur->m_Fn == fn )
  126. return true;
  127. pCur = pCur->m_pNext;
  128. }
  129. }
  130. return false;
  131. }
  132. inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
  133. {
  134. if ( !pStructBase )
  135. return 0;
  136. const SendProp *pProp = m_pPropMapStackPrecalc->GetDatatableProp( iProp );
  137. if ( IsNonPointerModifyingProxy( pProp->GetDataTableProxyFn(), m_pSendProxies ) )
  138. {
  139. // Note: these are offset by 1 (see the constructor), otherwise it won't recurse
  140. // during the Init call because pCurStructBase is 0.
  141. return pStructBase + pProp->GetOffset();
  142. }
  143. else
  144. {
  145. return 0;
  146. }
  147. }
  148. virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
  149. {
  150. // Remember where the game code pointed us for this datatable's data so
  151. m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;
  152. for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
  153. {
  154. CSendNode *pCurChild = pNode->GetChild( iChild );
  155. unsigned char *pNewStructBase = NULL;
  156. if ( pStructBase )
  157. {
  158. pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
  159. }
  160. RecurseAndCallProxies( pCurChild, pNewStructBase );
  161. }
  162. }
  163. public:
  164. CSendTablePrecalc *m_pPropMapStackPrecalc;
  165. const CStandardSendProxies *m_pSendProxies;
  166. };
  167. void BuildPropOffsetToIndexMap( CSendTablePrecalc *pPrecalc, const CStandardSendProxies *pSendProxies )
  168. {
  169. CPropMapStack pmStack( pPrecalc, pSendProxies );
  170. pmStack.Init( false, false );
  171. for ( int i=0; i < pPrecalc->m_Props.Count(); i++ )
  172. {
  173. pmStack.SeekToProp( i );
  174. if ( pmStack.GetCurStructBase() != 0 )
  175. {
  176. const SendProp *pProp = pPrecalc->m_Props[i];
  177. int offset = size_cast< int >( pProp->GetOffset() + (intp)pmStack.GetCurStructBase() - 1 );
  178. int elementCount = 1;
  179. int elementStride = 0;
  180. if ( pProp->GetType() == DPT_Array )
  181. {
  182. offset = size_cast< int >( pProp->GetArrayProp()->GetOffset() + (intp)pmStack.GetCurStructBase() - 1 );
  183. elementCount = pProp->m_nElements;
  184. elementStride = pProp->m_ElementStride;
  185. }
  186. if ( offset != 0 )
  187. {
  188. for ( int j = 0; j < elementCount; j++ )
  189. {
  190. if ( pProp->GetType() == DPT_Vector )
  191. {
  192. /*
  193. // Disabled this, the warning is benign -Zoid
  194. if ( !pProp->AreNetworkVarFlagsSet( NETWORKVAR_IS_A_VECTOR ) )
  195. {
  196. // TODO: This warning can probably go away. It shows up frequently when using SENDINFO_STRUCTARRAYELEM
  197. // or a CNetworkArray of Vectors, which aren't using CNetworkVector but will report their change offsets properly.
  198. Warning( "SendProp %s::%s is referencing a non-CNetworkVector-based network var\n", pPrecalc->m_pSendTable->GetName(), pProp->GetName() );
  199. }
  200. */
  201. if ( pProp->AreNetworkVarFlagsSet( NETWORKVAR_VECTOR_XYZ_FLAG ) )
  202. {
  203. // The CNetworkVarXYZ will generate change offsets for each component of the vector,
  204. // and we want all 3 change offsets to point right back to us.
  205. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  206. AddPropOffsetToMap( pPrecalc, i, offset + sizeof(float), pProp->m_pVarName );
  207. AddPropOffsetToMap( pPrecalc, i, offset + sizeof(float) * 2, pProp->m_pVarName );
  208. }
  209. else if ( pProp->AreNetworkVarFlagsSet( NETWORKVAR_VECTOR_XY_SEPARATEZ_FLAG ) )
  210. {
  211. // The CNetworkVarXY_SeparateZ will generate change offsets for &x (which represents changes to X or Y),
  212. // and &z. We want both change offsets to point right back to us.
  213. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  214. AddPropOffsetToMap( pPrecalc, i, offset + sizeof(float) * 2, pProp->m_pVarName );
  215. }
  216. else
  217. {
  218. // We're referenced by a CNetworkVector, which will only generate change offsets
  219. // at the offset of the variable itself (not for each component).
  220. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  221. }
  222. }
  223. else if ( pProp->GetType() == DPT_VectorXY )
  224. {
  225. if ( pProp->AreNetworkVarFlagsSet( NETWORKVAR_VECTOR_XYZ_FLAG ) )
  226. {
  227. // The CNetworkVarXYZ will generate change offsets for each component of the vector,
  228. // and we want the first 2 change offsets to point right back to us.
  229. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  230. AddPropOffsetToMap( pPrecalc, i, offset + sizeof(float), pProp->m_pVarName );
  231. }
  232. else if ( pProp->AreNetworkVarFlagsSet( NETWORKVAR_VECTOR_XY_SEPARATEZ_FLAG ) )
  233. {
  234. // The CNetworkVarXY_SeparateZ will generate change offsets for &x (which represents changes to X or Y),
  235. // and &z. We care about X and Y so we'll use the first one.
  236. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  237. }
  238. else
  239. {
  240. // We're referenced by a CNetworkVector, which will only generate change offsets
  241. // at the offset of the variable itself (not for each component).
  242. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  243. }
  244. }
  245. else
  246. {
  247. AddPropOffsetToMap( pPrecalc, i, offset, pProp->m_pVarName );
  248. }
  249. offset += elementStride;
  250. }
  251. }
  252. }
  253. }
  254. }
  255. void LocalTransfer_InitFastCopy(
  256. const SendTable *pSendTable,
  257. const CStandardSendProxies *pSendProxies,
  258. RecvTable *pRecvTable,
  259. const CStandardRecvProxies *pRecvProxies,
  260. int &nSlowCopyProps, // These are incremented to tell you how many fast copy props it found.
  261. int &nFastCopyProps
  262. )
  263. {
  264. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  265. // Setup the offset-to-index map.
  266. pPrecalc->m_PropOffsetToIndexMap.RemoveAll();
  267. BuildPropOffsetToIndexMap( pPrecalc, pSendProxies );
  268. // Clear the old lists.
  269. pPrecalc->m_FastLocalTransfer.m_FastInt32.Purge();
  270. pPrecalc->m_FastLocalTransfer.m_FastInt16.Purge();
  271. pPrecalc->m_FastLocalTransfer.m_FastInt8.Purge();
  272. pPrecalc->m_FastLocalTransfer.m_FastVector.Purge();
  273. pPrecalc->m_FastLocalTransfer.m_OtherProps.Purge();
  274. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  275. int iNumProp = pPrecalc->GetNumProps();
  276. for ( int iProp=0; iProp < iNumProp; iProp++ )
  277. {
  278. const SendProp *pSendProp = pPrecalc->GetProp( iProp );
  279. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  280. if ( pRecvProp )
  281. {
  282. Assert( stricmp( pSendProp->GetName(), pRecvProp->GetName() ) == 0 );
  283. CUtlVector<CFastLocalTransferPropInfo> *pList = &pPrecalc->m_FastLocalTransfer.m_OtherProps;
  284. if ( pSendProp->GetType() == DPT_Int &&
  285. (pSendProp->GetProxyFn() == pSendProxies->m_Int32ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt32ToInt32) &&
  286. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt32 )
  287. {
  288. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt32;
  289. ++nFastCopyProps;
  290. }
  291. else if( pSendProp->GetType() == DPT_Int &&
  292. (pSendProp->GetProxyFn() == pSendProxies->m_Int16ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt16ToInt32) &&
  293. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt16 )
  294. {
  295. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt16;
  296. ++nFastCopyProps;
  297. }
  298. else if( pSendProp->GetType() == DPT_Int &&
  299. (pSendProp->GetProxyFn() == pSendProxies->m_Int8ToInt32 || pSendProp->GetProxyFn() == pSendProxies->m_UInt8ToInt32) &&
  300. pRecvProp->GetProxyFn() == pRecvProxies->m_Int32ToInt8 )
  301. {
  302. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt8;
  303. ++nFastCopyProps;
  304. }
  305. else if( pSendProp->GetType() == DPT_Float &&
  306. pSendProp->GetProxyFn() == pSendProxies->m_FloatToFloat &&
  307. pRecvProp->GetProxyFn() == pRecvProxies->m_FloatToFloat )
  308. {
  309. Assert( sizeof( int ) == sizeof( float ) );
  310. pList = &pPrecalc->m_FastLocalTransfer.m_FastInt32;
  311. ++nFastCopyProps;
  312. }
  313. else if ( pSendProp->GetType() == DPT_Vector &&
  314. pSendProp->GetProxyFn() == pSendProxies->m_VectorToVector &&
  315. pRecvProp->GetProxyFn() == pRecvProxies->m_VectorToVector )
  316. {
  317. pList = &pPrecalc->m_FastLocalTransfer.m_FastVector;
  318. ++nFastCopyProps;
  319. }
  320. else
  321. {
  322. ++nSlowCopyProps;
  323. }
  324. CFastLocalTransferPropInfo toAdd;
  325. toAdd.m_iProp = iProp;
  326. toAdd.m_iRecvOffset = pRecvProp->GetOffset();
  327. toAdd.m_iSendOffset = pSendProp->GetOffset();
  328. pList->AddToTail( toAdd );
  329. }
  330. }
  331. }
  332. static inline bool FindInList( uint16 *pList, int nCount, uint16 lookFor )
  333. {
  334. for ( int i=0; i < nCount; i++ )
  335. {
  336. if ( pList[i] == lookFor )
  337. return true;
  338. }
  339. return false;
  340. }
  341. // This returns at most MAX_PROP_OFFSET_TO_INDICES_RESULTS results into pOut, so make sure it has at least that much room.
  342. inline int MapPropOffsetsToIndices(
  343. const CBaseEdict *pEdict,
  344. CSendTablePrecalc *pPrecalc,
  345. const unsigned short *pOffsets,
  346. unsigned short nOffsets,
  347. unsigned short *pOut )
  348. {
  349. // We could get an overflow if this happens.
  350. Assert( nOffsets <= MAX_CHANGE_OFFSETS );
  351. int nOut = 0;
  352. for ( unsigned short i=0; i < nOffsets; i++ )
  353. {
  354. unsigned short index = pPrecalc->m_PropOffsetToIndexMap.Find( pOffsets[i] );
  355. if ( index == pPrecalc->m_PropOffsetToIndexMap.InvalidIndex() )
  356. {
  357. // Note: this SHOULD be fine. In all known cases, when NetworkStateChanged is called with
  358. // an offset, there should be a corresponding SendProp in order for that NetworkStateChanged
  359. // call to mean anything.
  360. //
  361. // It means that if we can't find an offset here, then there isn't a SendProp
  362. // associated with the CNetworkVar that triggered the change. Therefore,
  363. // the change doesn't matter and we can skip past it.
  364. //
  365. // If we wanted to be anal, we could force them to use DISABLE_NETWORK_VAR_FOR_DERIVED
  366. // appropriately in all these cases, but then we'd need a ton of them for certain classes
  367. // (like CBaseViewModel, which has a slew of CNetworkVars in its base classes that
  368. // it doesn't want to transmit).
  369. if ( dt_ShowPartialChangeEnts.GetInt() )
  370. {
  371. static CUtlDict<int,int> testDict;
  372. char str[512];
  373. Q_snprintf( str, sizeof( str ), "LocalTransfer offset miss - class: %s, DT: %s, offset: %d", pEdict->GetClassName(), pPrecalc->m_pSendTable->m_pNetTableName, pOffsets[i] );
  374. if ( testDict.Find( str ) == testDict.InvalidIndex() )
  375. {
  376. testDict.Insert( str );
  377. Warning( "%s\n", str );
  378. }
  379. }
  380. }
  381. else
  382. {
  383. const PropIndicesCollection_t &coll = pPrecalc->m_PropOffsetToIndexMap[index];
  384. for ( int nIndex=0; nIndex < ARRAYSIZE( coll.m_Indices ); nIndex++ )
  385. {
  386. unsigned short propIndex = coll.m_Indices[nIndex];
  387. if ( propIndex == 0xFFFF )
  388. {
  389. continue;
  390. }
  391. else
  392. {
  393. if ( !FindInList( pOut, nOut, propIndex ) )
  394. {
  395. if ( nOut >= MAX_PROP_OFFSET_TO_INDICES_RESULTS )
  396. Error( "Overflowed output list in MapPropOffsetsToIndices" );
  397. pOut[nOut++] = propIndex;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. return nOut;
  404. }
  405. inline void FastSortList( unsigned short *pList, unsigned short nEntries )
  406. {
  407. if ( nEntries == 1 )
  408. return;
  409. unsigned short i = 0;
  410. while ( 1 )
  411. {
  412. if ( pList[i+1] < pList[i] )
  413. {
  414. unsigned short tmp = pList[i+1];
  415. pList[i+1] = pList[i];
  416. pList[i] = tmp;
  417. if ( i > 0 )
  418. --i;
  419. }
  420. else
  421. {
  422. ++i;
  423. if ( i >= (nEntries-1) )
  424. return;
  425. }
  426. }
  427. }
  428. inline void AddToPartialChangeEntsList( int iEnt, bool bPartial )
  429. {
  430. if ( !dt_ShowPartialChangeEnts.GetInt() )
  431. return;
  432. if ( !bPartial )
  433. iEnt = -iEnt;
  434. if ( g_PartialChangeEnts.Find( iEnt ) == -1 )
  435. g_PartialChangeEnts.AddToTail( iEnt );
  436. }
  437. void PrintPartialChangeEntsList()
  438. {
  439. if ( !dt_ShowPartialChangeEnts.GetInt() )
  440. return;
  441. int iCurRow = 15;
  442. Con_NPrintf( iCurRow++, "----- dt_ShowPartialChangeEnts -----" );
  443. Con_NPrintf( iCurRow++, "" );
  444. Con_NPrintf( iCurRow++, "Ent changes: %3d, prop changes: %3d",
  445. g_nTotalEntChanges, g_nTotalPropChanges );
  446. Con_NPrintf( iCurRow++, "" );
  447. char str[512];
  448. bool bFirst = true;
  449. // Write the partial ents.
  450. str[0] = 0;
  451. for ( int i=0; i < g_PartialChangeEnts.Count(); i++ )
  452. {
  453. if ( g_PartialChangeEnts[i] >= 0 )
  454. {
  455. if ( !bFirst )
  456. Q_strncat( str, ", ", sizeof( str ) );
  457. char tempStr[512];
  458. Q_snprintf( tempStr, sizeof( tempStr ), "%d", g_PartialChangeEnts[i] );
  459. Q_strncat( str, tempStr, sizeof( str ) );
  460. bFirst = false;
  461. }
  462. }
  463. Q_strncat( str, " - PARTIAL", sizeof( str ) );
  464. Con_NPrintf( iCurRow++, "%s", str );
  465. // Write the full ents.
  466. bFirst = true;
  467. str[0] = 0;
  468. for ( int i=0; i < g_PartialChangeEnts.Count(); i++ )
  469. {
  470. if ( g_PartialChangeEnts[i] < 0 )
  471. {
  472. if ( !bFirst )
  473. Q_strncat( str, ", ", sizeof( str ) );
  474. char tempStr[512];
  475. Q_snprintf( tempStr, sizeof( tempStr ), "%d", -g_PartialChangeEnts[i] );
  476. Q_strncat( str, tempStr, sizeof( str ) );
  477. bFirst = false;
  478. }
  479. }
  480. Q_strncat( str, " - FULL", sizeof( str ) );
  481. Con_NPrintf( iCurRow++, "%s", str );
  482. g_PartialChangeEnts.Purge();
  483. g_nTotalPropChanges = g_nTotalEntChanges = 0;
  484. }
  485. void LocalTransfer_TransferEntity(
  486. const CBaseEdict *pEdict,
  487. const SendTable *pSendTable,
  488. const void *pSrcEnt,
  489. RecvTable *pRecvTable,
  490. void *pDestEnt,
  491. bool bNewlyCreated,
  492. bool bJustEnteredPVS,
  493. int objectID )
  494. {
  495. ++g_nTotalEntChanges;
  496. CEdictChangeInfo *pCI = &g_pSharedChangeInfo->m_ChangeInfos[pEdict->GetChangeInfo()];
  497. unsigned short propIndices[MAX_PROP_OFFSET_TO_INDICES_RESULTS];
  498. unsigned char tempData[ sizeof( CSendProxyRecipients ) * MAX_DATATABLE_PROXIES ];
  499. CUtlMemory< CSendProxyRecipients > recip( (CSendProxyRecipients*)tempData, pSendTable->m_pPrecalc->GetNumDataTableProxies() );
  500. // This code tries to only copy fields expressly marked as "changed" (by having the field offsets added to the changeoffsets vectors)
  501. if ( pEdict->GetChangeInfoSerialNumber() == g_pSharedChangeInfo->m_iSerialNumber &&
  502. !bNewlyCreated &&
  503. !bJustEnteredPVS &&
  504. dt_UsePartialChangeEnts.GetInt()
  505. )
  506. {
  507. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  508. int nChangeOffsets = MapPropOffsetsToIndices( pEdict, pPrecalc, pCI->m_ChangeOffsets, pCI->m_nChangeOffsets, propIndices );
  509. if ( nChangeOffsets == 0 )
  510. return;
  511. AddToPartialChangeEntsList( (edict_t*)pEdict - sv.edicts, true );
  512. FastSortList( propIndices, nChangeOffsets );
  513. // Setup the structure to traverse the source tree.
  514. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pSendTable->m_pNetTableName) );
  515. CServerDatatableStack serverStack( pPrecalc, (unsigned char*)pSrcEnt, objectID, &recip );
  516. serverStack.Init( true, true );
  517. // Setup the structure to traverse the dest tree.
  518. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  519. ErrorIfNot( pDecoder, ("RecvTable_Decode: table '%s' missing a decoder.", pRecvTable->GetName()) );
  520. CClientDatatableStack clientStack( pDecoder, (unsigned char*)pDestEnt, objectID );
  521. clientStack.Init( true, true );
  522. // Cool. We can get away with just transferring a few.
  523. for ( int iChanged=0; iChanged < nChangeOffsets; iChanged++ )
  524. {
  525. int iProp = propIndices[iChanged];
  526. ++g_nTotalPropChanges;
  527. serverStack.SeekToProp( iProp );
  528. const SendProp *pSendProp = serverStack.GetCurProp();
  529. unsigned char *pSendBase = serverStack.UpdateRoutesExplicit();
  530. if ( pSendBase )
  531. {
  532. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  533. Assert( pRecvProp );
  534. clientStack.SeekToProp( iProp );
  535. unsigned char *pRecvBase = clientStack.UpdateRoutesExplicit();
  536. Assert( pRecvBase );
  537. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  538. }
  539. }
  540. }
  541. // Whereas the below code copies _all_ fields, regardless of whether they were changed or not. We run this only newly created entities, or entities
  542. // 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
  543. // frame.
  544. else
  545. {
  546. // Setup the structure to traverse the source tree.
  547. CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
  548. ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pSendTable->m_pNetTableName) );
  549. CServerDatatableStack serverStack( pPrecalc, (unsigned char*)pSrcEnt, objectID, &recip );
  550. serverStack.Init( false, true );
  551. // Setup the structure to traverse the dest tree.
  552. CRecvDecoder *pDecoder = pRecvTable->m_pDecoder;
  553. ErrorIfNot( pDecoder, ("RecvTable_Decode: table '%s' missing a decoder.", pRecvTable->GetName()) );
  554. CClientDatatableStack clientStack( pDecoder, (unsigned char*)pDestEnt, objectID );
  555. clientStack.Init( false, true );
  556. AddToPartialChangeEntsList( (edict_t*)pEdict - sv.edicts, false );
  557. // Copy the properties that require proxies.
  558. CFastLocalTransferPropInfo *pPropList = pPrecalc->m_FastLocalTransfer.m_OtherProps.Base();
  559. int nProps = pPrecalc->m_FastLocalTransfer.m_OtherProps.Count();
  560. for ( int i=0; i < nProps; i++ )
  561. {
  562. int iProp = pPropList[i].m_iProp;
  563. serverStack.SeekToProp( iProp );
  564. const SendProp *pSendProp = serverStack.GetCurProp();
  565. unsigned char *pSendBase = serverStack.GetCurStructBase();
  566. if ( pSendBase )
  567. {
  568. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  569. Assert( pRecvProp );
  570. clientStack.SeekToProp( iProp );
  571. unsigned char *pRecvBase = clientStack.GetCurStructBase();
  572. Assert( pRecvBase );
  573. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  574. }
  575. }
  576. // Transfer over the fast properties.
  577. LocalTransfer_FastType( (int*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt32.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt32.Count() );
  578. LocalTransfer_FastType( (short*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt16.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt16.Count() );
  579. LocalTransfer_FastType( (char*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastInt8.Base(), pPrecalc->m_FastLocalTransfer.m_FastInt8.Count() );
  580. LocalTransfer_FastType( (Vector*)0, serverStack, clientStack, pPrecalc->m_FastLocalTransfer.m_FastVector.Base(), pPrecalc->m_FastLocalTransfer.m_FastVector.Count() );
  581. }
  582. // The old, slow method to copy all props using their proxies.
  583. /*
  584. int iEndProp = pPrecalc->m_Root.GetLastPropIndex();
  585. for ( int iProp=0; iProp <= iEndProp; iProp++ )
  586. {
  587. serverStack.SeekToProp( iProp );
  588. clientStack.SeekToProp( iProp );
  589. const SendProp *pSendProp = serverStack.GetCurProp();
  590. const RecvProp *pRecvProp = pDecoder->GetProp( iProp );
  591. if ( pRecvProp )
  592. {
  593. unsigned char *pSendBase = serverStack.GetCurStructBase();
  594. unsigned char *pRecvBase = clientStack.GetCurStructBase();
  595. if ( pSendBase && pRecvBase )
  596. {
  597. Assert( stricmp( pSendProp->GetName(), pRecvProp->GetName() ) == 0 );
  598. g_PropTypeFns[pRecvProp->GetType()].FastCopy( pSendProp, pRecvProp, pSendBase, pRecvBase, objectID );
  599. }
  600. }
  601. }
  602. */
  603. }