//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include "server.h"
# include "dt_stack.h"
# include "dt_localtransfer.h"
# include "mathlib/vector.h"
# include "edict.h"
# include "convar.h"
# include "con_nprint.h"
# include "utldict.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# define PROP_INDEX_VECTOR_ELEM_MARKER 0x8000
static ConVar dt_UsePartialChangeEnts (
" dt_UsePartialChangeEnts " ,
" 1 " ,
0 ,
" (SP only) - enable FL_EDICT_PARTIAL_CHANGE optimization. "
) ;
static ConVar dt_ShowPartialChangeEnts (
" dt_ShowPartialChangeEnts " ,
" 0 " ,
0 ,
" (SP only) - show entities that were copied using small optimized lists (FL_EDICT_PARTIAL_CHANGE). "
) ;
// Negative numbers represent entities that were fully copied.
static CUtlVector < int > g_PartialChangeEnts ;
static int g_nTotalPropChanges = 0 ;
static int g_nTotalEntChanges = 0 ;
template < class T >
inline void LocalTransfer_FastType (
T * pBlah ,
CServerDatatableStack & serverStack ,
CClientDatatableStack & clientStack ,
CFastLocalTransferPropInfo * pPropList ,
int nProps )
{
CFastLocalTransferPropInfo * pCur = pPropList ;
for ( int i = 0 ; i < nProps ; i + + , pCur + + )
{
serverStack . SeekToProp ( pCur - > m_iProp ) ;
unsigned char * pServerBase = serverStack . GetCurStructBase ( ) ;
if ( pServerBase )
{
clientStack . SeekToProp ( pCur - > m_iProp ) ;
unsigned char * pClientBase = clientStack . GetCurStructBase ( ) ;
Assert ( pClientBase ) ;
const T * pSource = ( const T * ) ( pServerBase + pCur - > m_iSendOffset ) ;
T * pDest = ( T * ) ( pClientBase + pCur - > m_iRecvOffset ) ;
memcpy ( pDest , pSource , sizeof ( T ) ) ;
}
}
}
void AddPropOffsetToMap ( CSendTablePrecalc * pPrecalc , int iInProp , int iInOffset )
{
Assert ( iInProp < 0xFFFF & & iInOffset < 0xFFFF ) ;
unsigned short iProp = ( unsigned short ) iInProp ;
unsigned short iOffset = ( unsigned short ) iInOffset ;
unsigned short iOldIndex = pPrecalc - > m_PropOffsetToIndexMap . Find ( iOffset ) ;
if ( iOldIndex ! = pPrecalc - > m_PropOffsetToIndexMap . InvalidIndex ( ) )
{
return ;
}
pPrecalc - > m_PropOffsetToIndexMap . Insert ( iOffset , iProp ) ;
}
// This helps us figure out which properties can use the super-optimized mode
// where they are tracked in a list when they change. If their m_pProxies pointers
// are set to 1, then it means that this property is gotten to by means of SendProxy_DataTableToDataTable.
// If it's set to 0, then we can't directly take the property's offset.
class CPropMapStack : public CDatatableStack
{
public :
CPropMapStack ( CSendTablePrecalc * pPrecalc , const CStandardSendProxies * pSendProxies ) :
CDatatableStack ( pPrecalc , ( unsigned char * ) 1 , - 1 )
{
m_pPropMapStackPrecalc = pPrecalc ;
m_pSendProxies = pSendProxies ;
}
bool IsNonPointerModifyingProxy ( SendTableProxyFn fn , const CStandardSendProxies * pSendProxies )
{
if ( fn = = m_pSendProxies - > m_DataTableToDataTable | |
fn = = m_pSendProxies - > m_SendLocalDataTable )
{
return true ;
}
if ( pSendProxies - > m_ppNonModifiedPointerProxies )
{
CNonModifiedPointerProxy * pCur = * pSendProxies - > m_ppNonModifiedPointerProxies ;
while ( pCur )
{
if ( pCur - > m_Fn = = fn )
return true ;
pCur = pCur - > m_pNext ;
}
}
return false ;
}
inline unsigned char * CallPropProxy ( CSendNode * pNode , int iProp , unsigned char * pStructBase )
{
if ( ! pStructBase )
return 0 ;
const SendProp * pProp = m_pPropMapStackPrecalc - > GetDatatableProp ( iProp ) ;
if ( IsNonPointerModifyingProxy ( pProp - > GetDataTableProxyFn ( ) , m_pSendProxies ) )
{
// Note: these are offset by 1 (see the constructor), otherwise it won't recurse
// during the Init call because pCurStructBase is 0.
return pStructBase + pProp - > GetOffset ( ) ;
}
else
{
return 0 ;
}
}
virtual void RecurseAndCallProxies ( CSendNode * pNode , unsigned char * pStructBase )
{
// Remember where the game code pointed us for this datatable's data so
m_pProxies [ pNode - > GetRecursiveProxyIndex ( ) ] = pStructBase ;
for ( int iChild = 0 ; iChild < pNode - > GetNumChildren ( ) ; iChild + + )
{
CSendNode * pCurChild = pNode - > GetChild ( iChild ) ;
unsigned char * pNewStructBase = NULL ;
if ( pStructBase )
{
pNewStructBase = CallPropProxy ( pCurChild , pCurChild - > m_iDatatableProp , pStructBase ) ;
}
RecurseAndCallProxies ( pCurChild , pNewStructBase ) ;
}
}
public :
CSendTablePrecalc * m_pPropMapStackPrecalc ;
const CStandardSendProxies * m_pSendProxies ;
} ;
void BuildPropOffsetToIndexMap ( CSendTablePrecalc * pPrecalc , const CStandardSendProxies * pSendProxies )
{
CPropMapStack pmStack ( pPrecalc , pSendProxies ) ;
pmStack . Init ( ) ;
for ( int i = 0 ; i < pPrecalc - > m_Props . Count ( ) ; i + + )
{
pmStack . SeekToProp ( i ) ;
if ( pmStack . GetCurStructBase ( ) ! = 0 )
{
const SendProp * pProp = pPrecalc - > m_Props [ i ] ;
intp offset = pProp - > GetOffset ( ) + ( intp ) pmStack . GetCurStructBase ( ) - 1 ;
int elementCount = 1 ;
int elementStride = 0 ;
if ( pProp - > GetType ( ) = = DPT_Array )
{
offset = pProp - > GetArrayProp ( ) - > GetOffset ( ) + ( intp ) pmStack . GetCurStructBase ( ) - 1 ;
elementCount = pProp - > m_nElements ;
elementStride = pProp - > m_ElementStride ;
}
if ( offset ! = 0 )
{
for ( int j = 0 ; j < elementCount ; j + + )
{
if ( pProp - > GetFlags ( ) & SPROP_IS_A_VECTOR_ELEM )
{
AddPropOffsetToMap ( pPrecalc , i | PROP_INDEX_VECTOR_ELEM_MARKER , offset ) ;
}
else
{
AddPropOffsetToMap ( pPrecalc , i , offset ) ;
}
offset + = elementStride ;
}
}
}
}
}
void LocalTransfer_InitFastCopy (
const SendTable * pSendTable ,
const CStandardSendProxies * pSendProxies ,
RecvTable * pRecvTable ,
const CStandardRecvProxies * pRecvProxies ,
int & nSlowCopyProps , // These are incremented to tell you how many fast copy props it found.
int & nFastCopyProps
)
{
CSendTablePrecalc * pPrecalc = pSendTable - > m_pPrecalc ;
// Setup the offset-to-index map.
pPrecalc - > m_PropOffsetToIndexMap . RemoveAll ( ) ;
BuildPropOffsetToIndexMap ( pPrecalc , pSendProxies ) ;
// Clear the old lists.
pPrecalc - > m_FastLocalTransfer . m_FastInt32 . Purge ( ) ;
pPrecalc - > m_FastLocalTransfer . m_FastInt16 . Purge ( ) ;
pPrecalc - > m_FastLocalTransfer . m_FastInt8 . Purge ( ) ;
pPrecalc - > m_FastLocalTransfer . m_FastVector . Purge ( ) ;
pPrecalc - > m_FastLocalTransfer . m_OtherProps . Purge ( ) ;
CRecvDecoder * pDecoder = pRecvTable - > m_pDecoder ;
int iNumProp = pPrecalc - > GetNumProps ( ) ;
for ( int iProp = 0 ; iProp < iNumProp ; iProp + + )
{
const SendProp * pSendProp = pPrecalc - > GetProp ( iProp ) ;
const RecvProp * pRecvProp = pDecoder - > GetProp ( iProp ) ;
if ( pRecvProp )
{
Assert ( stricmp ( pSendProp - > GetName ( ) , pRecvProp - > GetName ( ) ) = = 0 ) ;
CUtlVector < CFastLocalTransferPropInfo > * pList = & pPrecalc - > m_FastLocalTransfer . m_OtherProps ;
if ( pSendProp - > GetType ( ) = = DPT_Int & &
( pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_Int32ToInt32 | | pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_UInt32ToInt32 ) & &
pRecvProp - > GetProxyFn ( ) = = pRecvProxies - > m_Int32ToInt32 )
{
pList = & pPrecalc - > m_FastLocalTransfer . m_FastInt32 ;
+ + nFastCopyProps ;
}
else if ( pSendProp - > GetType ( ) = = DPT_Int & &
( pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_Int16ToInt32 | | pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_UInt16ToInt32 ) & &
pRecvProp - > GetProxyFn ( ) = = pRecvProxies - > m_Int32ToInt16 )
{
pList = & pPrecalc - > m_FastLocalTransfer . m_FastInt16 ;
+ + nFastCopyProps ;
}
else if ( pSendProp - > GetType ( ) = = DPT_Int & &
( pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_Int8ToInt32 | | pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_UInt8ToInt32 ) & &
pRecvProp - > GetProxyFn ( ) = = pRecvProxies - > m_Int32ToInt8 )
{
pList = & pPrecalc - > m_FastLocalTransfer . m_FastInt8 ;
+ + nFastCopyProps ;
}
else if ( pSendProp - > GetType ( ) = = DPT_Float & &
pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_FloatToFloat & &
pRecvProp - > GetProxyFn ( ) = = pRecvProxies - > m_FloatToFloat )
{
Assert ( sizeof ( int ) = = sizeof ( float ) ) ;
pList = & pPrecalc - > m_FastLocalTransfer . m_FastInt32 ;
+ + nFastCopyProps ;
}
else if ( pSendProp - > GetType ( ) = = DPT_Vector & &
pSendProp - > GetProxyFn ( ) = = pSendProxies - > m_VectorToVector & &
pRecvProp - > GetProxyFn ( ) = = pRecvProxies - > m_VectorToVector )
{
pList = & pPrecalc - > m_FastLocalTransfer . m_FastVector ;
+ + nFastCopyProps ;
}
else
{
+ + nSlowCopyProps ;
}
CFastLocalTransferPropInfo toAdd ;
toAdd . m_iProp = iProp ;
toAdd . m_iRecvOffset = pRecvProp - > GetOffset ( ) ;
toAdd . m_iSendOffset = pSendProp - > GetOffset ( ) ;
pList - > AddToTail ( toAdd ) ;
}
}
}
inline int MapPropOffsetsToIndices (
const CBaseEdict * pEdict ,
CSendTablePrecalc * pPrecalc ,
const unsigned short * pOffsets ,
unsigned short nOffsets ,
unsigned short * pOut )
{
int iOut = 0 ;
for ( unsigned short i = 0 ; i < nOffsets ; i + + )
{
unsigned short index = pPrecalc - > m_PropOffsetToIndexMap . Find ( pOffsets [ i ] ) ;
if ( index = = pPrecalc - > m_PropOffsetToIndexMap . InvalidIndex ( ) )
{
// Note: this SHOULD be fine. In all known cases, when NetworkStateChanged is called with
// an offset, there should be a corresponding SendProp in order for that NetworkStateChanged
// call to mean anything.
//
// It means that if we can't find an offset here, then there isn't a SendProp
// associated with the CNetworkVar that triggered the change. Therefore,
// the change doesn't matter and we can skip past it.
//
// If we wanted to be anal, we could force them to use DISABLE_NETWORK_VAR_FOR_DERIVED
// appropriately in all these cases, but then we'd need a ton of them for certain classes
// (like CBaseViewModel, which has a slew of CNetworkVars in its base classes that
// it doesn't want to transmit).
if ( dt_ShowPartialChangeEnts . GetInt ( ) )
{
static CUtlDict < int , int > testDict ;
char str [ 512 ] ;
Q_snprintf ( str , sizeof ( str ) , " LocalTransfer offset miss - class: %s, DT: %s, offset: %d " , pEdict - > GetClassName ( ) , pPrecalc - > m_pSendTable - > m_pNetTableName , pOffsets [ i ] ) ;
if ( testDict . Find ( str ) = = testDict . InvalidIndex ( ) )
{
testDict . Insert ( str ) ;
Warning ( " %s \n " , str ) ;
}
}
}
else
{
unsigned short propIndex = pPrecalc - > m_PropOffsetToIndexMap [ index ] ;
if ( propIndex & PROP_INDEX_VECTOR_ELEM_MARKER )
{
// Look for all 3 vector elems here.
unsigned short curOffset = pOffsets [ i ] ;
for ( int iVectorElem = 0 ; iVectorElem < 3 ; iVectorElem + + )
{
index = pPrecalc - > m_PropOffsetToIndexMap . Find ( curOffset ) ;
if ( index = = 0xFFFF )
{
break ;
}
else
{
propIndex = pPrecalc - > m_PropOffsetToIndexMap [ index ] ;
if ( propIndex & PROP_INDEX_VECTOR_ELEM_MARKER )
pOut [ iOut + + ] = ( propIndex & ~ PROP_INDEX_VECTOR_ELEM_MARKER ) ;
}
curOffset + = sizeof ( float ) ;
}
}
else
{
pOut [ iOut + + ] = propIndex ;
}
}
}
return iOut ;
}
inline void FastSortList ( unsigned short * pList , unsigned short nEntries )
{
if ( nEntries = = 1 )
return ;
unsigned short i = 0 ;
while ( 1 )
{
if ( pList [ i + 1 ] < pList [ i ] )
{
unsigned short tmp = pList [ i + 1 ] ;
pList [ i + 1 ] = pList [ i ] ;
pList [ i ] = tmp ;
if ( i > 0 )
- - i ;
}
else
{
+ + i ;
if ( i > = ( nEntries - 1 ) )
return ;
}
}
}
inline void AddToPartialChangeEntsList ( int iEnt , bool bPartial )
{
if ( ! dt_ShowPartialChangeEnts . GetInt ( ) )
return ;
# if !defined( _XBOX )
if ( ! bPartial )
iEnt = - iEnt ;
if ( g_PartialChangeEnts . Find ( iEnt ) = = - 1 )
g_PartialChangeEnts . AddToTail ( iEnt ) ;
# endif
}
void PrintPartialChangeEntsList ( )
{
if ( ! dt_ShowPartialChangeEnts . GetInt ( ) )
return ;
# if !defined( _XBOX )
int iCurRow = 15 ;
Con_NPrintf ( iCurRow + + , " ----- dt_ShowPartialChangeEnts ----- " ) ;
Con_NPrintf ( iCurRow + + , " " ) ;
Con_NPrintf ( iCurRow + + , " Ent changes: %3d, prop changes: %3d " ,
g_nTotalEntChanges , g_nTotalPropChanges ) ;
Con_NPrintf ( iCurRow + + , " " ) ;
char str [ 512 ] ;
bool bFirst = true ;
// Write the partial ents.
str [ 0 ] = 0 ;
for ( int i = 0 ; i < g_PartialChangeEnts . Count ( ) ; i + + )
{
if ( g_PartialChangeEnts [ i ] > = 0 )
{
if ( ! bFirst )
Q_strncat ( str , " , " , sizeof ( str ) ) ;
char tempStr [ 512 ] ;
Q_snprintf ( tempStr , sizeof ( tempStr ) , " %d " , g_PartialChangeEnts [ i ] ) ;
Q_strncat ( str , tempStr , sizeof ( str ) ) ;
bFirst = false ;
}
}
Q_strncat ( str , " - PARTIAL " , sizeof ( str ) ) ;
Con_NPrintf ( iCurRow + + , " %s " , str ) ;
// Write the full ents.
bFirst = true ;
str [ 0 ] = 0 ;
for ( int i = 0 ; i < g_PartialChangeEnts . Count ( ) ; i + + )
{
if ( g_PartialChangeEnts [ i ] < 0 )
{
if ( ! bFirst )
Q_strncat ( str , " , " , sizeof ( str ) ) ;
char tempStr [ 512 ] ;
Q_snprintf ( tempStr , sizeof ( tempStr ) , " %d " , - g_PartialChangeEnts [ i ] ) ;
Q_strncat ( str , tempStr , sizeof ( str ) ) ;
bFirst = false ;
}
}
Q_strncat ( str , " - FULL " , sizeof ( str ) ) ;
Con_NPrintf ( iCurRow + + , " %s " , str ) ;
g_PartialChangeEnts . Purge ( ) ;
g_nTotalPropChanges = g_nTotalEntChanges = 0 ;
# endif
}
void LocalTransfer_TransferEntity (
const CBaseEdict * pEdict ,
const SendTable * pSendTable ,
const void * pSrcEnt ,
RecvTable * pRecvTable ,
void * pDestEnt ,
bool bNewlyCreated ,
bool bJustEnteredPVS ,
int objectID )
{
+ + g_nTotalEntChanges ;
CEdictChangeInfo * pCI = & g_pSharedChangeInfo - > m_ChangeInfos [ pEdict - > GetChangeInfo ( ) ] ;
unsigned short propIndices [ MAX_CHANGE_OFFSETS * 3 ] ;
// This code tries to only copy fields expressly marked as "changed" (by having the field offsets added to the changeoffsets vectors)
if ( pEdict - > GetChangeInfoSerialNumber ( ) = = g_pSharedChangeInfo - > m_iSerialNumber & &
! bNewlyCreated & &
! bJustEnteredPVS & &
dt_UsePartialChangeEnts . GetInt ( )
)
{
CSendTablePrecalc * pPrecalc = pSendTable - > m_pPrecalc ;
int nChangeOffsets = MapPropOffsetsToIndices ( pEdict , pPrecalc , pCI - > m_ChangeOffsets , pCI - > m_nChangeOffsets , propIndices ) ;
if ( nChangeOffsets = = 0 )
return ;
AddToPartialChangeEntsList ( ( edict_t * ) pEdict - sv . edicts , true ) ;
FastSortList ( propIndices , nChangeOffsets ) ;
// Setup the structure to traverse the source tree.
ErrorIfNot ( pPrecalc , ( " SendTable_Encode: Missing m_pPrecalc for SendTable %s. " , pSendTable - > m_pNetTableName ) ) ;
CServerDatatableStack serverStack ( pPrecalc , ( unsigned char * ) pSrcEnt , objectID ) ;
serverStack . Init ( true ) ;
// Setup the structure to traverse the dest tree.
CRecvDecoder * pDecoder = pRecvTable - > m_pDecoder ;
ErrorIfNot ( pDecoder , ( " RecvTable_Decode: table '%s' missing a decoder. " , pRecvTable - > GetName ( ) ) ) ;
CClientDatatableStack clientStack ( pDecoder , ( unsigned char * ) pDestEnt , objectID ) ;
clientStack . Init ( true ) ;
// Cool. We can get away with just transferring a few.
for ( int iChanged = 0 ; iChanged < nChangeOffsets ; iChanged + + )
{
int iProp = propIndices [ iChanged ] ;
+ + g_nTotalPropChanges ;
serverStack . SeekToProp ( iProp ) ;
const SendProp * pSendProp = serverStack . GetCurProp ( ) ;
unsigned char * pSendBase = serverStack . UpdateRoutesExplicit ( ) ;
if ( pSendBase )
{
const RecvProp * pRecvProp = pDecoder - > GetProp ( iProp ) ;
Assert ( pRecvProp ) ;
clientStack . SeekToProp ( iProp ) ;
unsigned char * pRecvBase = clientStack . UpdateRoutesExplicit ( ) ;
Assert ( pRecvBase ) ;
g_PropTypeFns [ pRecvProp - > GetType ( ) ] . FastCopy ( pSendProp , pRecvProp , pSendBase , pRecvBase , objectID ) ;
}
}
}
// Whereas the below code copies _all_ fields, regardless of whether they were changed or not. We run this only newly created entities, or entities
// 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
// frame.
else
{
// Setup the structure to traverse the source tree.
CSendTablePrecalc * pPrecalc = pSendTable - > m_pPrecalc ;
ErrorIfNot ( pPrecalc , ( " SendTable_Encode: Missing m_pPrecalc for SendTable %s. " , pSendTable - > m_pNetTableName ) ) ;
CServerDatatableStack serverStack ( pPrecalc , ( unsigned char * ) pSrcEnt , objectID ) ;
serverStack . Init ( ) ;
// Setup the structure to traverse the dest tree.
CRecvDecoder * pDecoder = pRecvTable - > m_pDecoder ;
ErrorIfNot ( pDecoder , ( " RecvTable_Decode: table '%s' missing a decoder. " , pRecvTable - > GetName ( ) ) ) ;
CClientDatatableStack clientStack ( pDecoder , ( unsigned char * ) pDestEnt , objectID ) ;
clientStack . Init ( ) ;
AddToPartialChangeEntsList ( ( edict_t * ) pEdict - sv . edicts , false ) ;
// Copy the properties that require proxies.
CFastLocalTransferPropInfo * pPropList = pPrecalc - > m_FastLocalTransfer . m_OtherProps . Base ( ) ;
int nProps = pPrecalc - > m_FastLocalTransfer . m_OtherProps . Count ( ) ;
for ( int i = 0 ; i < nProps ; i + + )
{
int iProp = pPropList [ i ] . m_iProp ;
serverStack . SeekToProp ( iProp ) ;
const SendProp * pSendProp = serverStack . GetCurProp ( ) ;
unsigned char * pSendBase = serverStack . GetCurStructBase ( ) ;
if ( pSendBase )
{
const RecvProp * pRecvProp = pDecoder - > GetProp ( iProp ) ;
Assert ( pRecvProp ) ;
clientStack . SeekToProp ( iProp ) ;
unsigned char * pRecvBase = clientStack . GetCurStructBase ( ) ;
Assert ( pRecvBase ) ;
g_PropTypeFns [ pRecvProp - > GetType ( ) ] . FastCopy ( pSendProp , pRecvProp , pSendBase , pRecvBase , objectID ) ;
}
}
// Transfer over the fast properties.
LocalTransfer_FastType ( ( int * ) 0 , serverStack , clientStack , pPrecalc - > m_FastLocalTransfer . m_FastInt32 . Base ( ) , pPrecalc - > m_FastLocalTransfer . m_FastInt32 . Count ( ) ) ;
LocalTransfer_FastType ( ( short * ) 0 , serverStack , clientStack , pPrecalc - > m_FastLocalTransfer . m_FastInt16 . Base ( ) , pPrecalc - > m_FastLocalTransfer . m_FastInt16 . Count ( ) ) ;
LocalTransfer_FastType ( ( char * ) 0 , serverStack , clientStack , pPrecalc - > m_FastLocalTransfer . m_FastInt8 . Base ( ) , pPrecalc - > m_FastLocalTransfer . m_FastInt8 . Count ( ) ) ;
LocalTransfer_FastType ( ( Vector * ) 0 , serverStack , clientStack , pPrecalc - > m_FastLocalTransfer . m_FastVector . Base ( ) , pPrecalc - > m_FastLocalTransfer . m_FastVector . Count ( ) ) ;
}
// The old, slow method to copy all props using their proxies.
/*
int iEndProp = pPrecalc - > m_Root . GetLastPropIndex ( ) ;
for ( int iProp = 0 ; iProp < = iEndProp ; iProp + + )
{
serverStack . SeekToProp ( iProp ) ;
clientStack . SeekToProp ( iProp ) ;
const SendProp * pSendProp = serverStack . GetCurProp ( ) ;
const RecvProp * pRecvProp = pDecoder - > GetProp ( iProp ) ;
if ( pRecvProp )
{
unsigned char * pSendBase = serverStack . GetCurStructBase ( ) ;
unsigned char * pRecvBase = clientStack . GetCurStructBase ( ) ;
if ( pSendBase & & pRecvBase )
{
Assert ( stricmp ( pSendProp - > GetName ( ) , pRecvProp - > GetName ( ) ) = = 0 ) ;
g_PropTypeFns [ pRecvProp - > GetType ( ) ] . FastCopy ( pSendProp , pRecvProp , pSendBase , pRecvBase , objectID ) ;
}
}
}
*/
}