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.
1045 lines
26 KiB
1045 lines
26 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include <tier0/dbg.h> |
|
#include <tier0/vprof.h> |
|
#include <tier0/icommandline.h> |
|
#include <commonmacros.h> |
|
#include <checksum_crc.h> |
|
|
|
#include "dt_send_eng.h" |
|
#include "dt_encode.h" |
|
#include "dt_instrumentation_server.h" |
|
#include "dt_stack.h" |
|
#include "common.h" |
|
#include "packed_entity.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
extern int host_framecount; |
|
CRC32_t SendTable_ComputeCRC(); |
|
|
|
class CSendTablePrecalc; |
|
class CSendNode; |
|
|
|
extern bool Sendprop_UsingDebugWatch(); |
|
|
|
|
|
// This stack doesn't actually call any proxies. It uses the CSendProxyRecipients to tell |
|
// what can be sent to the specified client. |
|
class CPropCullStack : public CDatatableStack |
|
{ |
|
public: |
|
CPropCullStack( |
|
CSendTablePrecalc *pPrecalc, |
|
int iClient, |
|
const CSendProxyRecipients *pOldStateProxies, |
|
const int nOldStateProxies, |
|
const CSendProxyRecipients *pNewStateProxies, |
|
const int nNewStateProxies |
|
) : |
|
|
|
CDatatableStack( pPrecalc, (unsigned char*)1, -1 ), |
|
m_pOldStateProxies( pOldStateProxies ), |
|
m_nOldStateProxies( nOldStateProxies ), |
|
m_pNewStateProxies( pNewStateProxies ), |
|
m_nNewStateProxies( nNewStateProxies ) |
|
{ |
|
m_pPrecalc = pPrecalc; |
|
m_iClient = iClient; |
|
} |
|
|
|
inline unsigned char* CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase ) |
|
{ |
|
if ( pNode->GetDataTableProxyIndex() == DATATABLE_PROXY_INDEX_NOPROXY ) |
|
{ |
|
return (unsigned char*)1; |
|
} |
|
else |
|
{ |
|
Assert( pNode->GetDataTableProxyIndex() < m_nNewStateProxies ); |
|
bool bCur = m_pNewStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0; |
|
|
|
if ( m_pOldStateProxies ) |
|
{ |
|
Assert( pNode->GetDataTableProxyIndex() < m_nOldStateProxies ); |
|
bool bPrev = m_pOldStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0; |
|
if ( bPrev != bCur ) |
|
{ |
|
if ( bPrev ) |
|
{ |
|
// Old state had the data and the new state doesn't. |
|
return 0; |
|
} |
|
else |
|
{ |
|
// Add ALL properties under this proxy because the previous state didn't have any of them. |
|
for ( int i=0; i < pNode->m_nRecursiveProps; i++ ) |
|
{ |
|
if ( m_nNewProxyProps < ARRAYSIZE( m_NewProxyProps ) ) |
|
{ |
|
m_NewProxyProps[m_nNewProxyProps] = pNode->m_iFirstRecursiveProp + i; |
|
} |
|
else |
|
{ |
|
Error( "CPropCullStack::CallPropProxy - overflowed m_nNewProxyProps" ); |
|
} |
|
|
|
++m_nNewProxyProps; |
|
} |
|
|
|
// Tell the outer loop that writes to m_pOutProps not to write anything from this |
|
// proxy since we just did. |
|
return 0; |
|
} |
|
} |
|
} |
|
|
|
return (unsigned char*)bCur; |
|
} |
|
} |
|
|
|
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 ); |
|
} |
|
} |
|
|
|
inline void AddProp( int iProp ) |
|
{ |
|
if ( m_nOutProps < m_nMaxOutProps ) |
|
{ |
|
m_pOutProps[m_nOutProps] = iProp; |
|
} |
|
else |
|
{ |
|
Error( "CPropCullStack::AddProp - m_pOutProps overflowed" ); |
|
} |
|
|
|
++m_nOutProps; |
|
} |
|
|
|
|
|
void CullPropsFromProxies( const int *pStartProps, int nStartProps, int *pOutProps, int nMaxOutProps ) |
|
{ |
|
m_nOutProps = 0; |
|
m_pOutProps = pOutProps; |
|
m_nMaxOutProps = nMaxOutProps; |
|
m_nNewProxyProps = 0; |
|
|
|
Init(); |
|
|
|
// This list will have any newly available props written into it. Write a sentinel at the end. |
|
m_NewProxyProps[m_nNewProxyProps] = -1; // invalid marker |
|
int *pCurNewProxyProp = m_NewProxyProps; |
|
|
|
for ( int i=0; i < nStartProps; i++ ) |
|
{ |
|
int iProp = pStartProps[i]; |
|
|
|
// Fill in the gaps with any properties that are newly enabled by the proxies. |
|
while ( (unsigned int) *pCurNewProxyProp < (unsigned int) iProp ) |
|
{ |
|
AddProp( *pCurNewProxyProp ); |
|
++pCurNewProxyProp; |
|
} |
|
|
|
// Now write this property's index if the proxies are allowing this property to be written. |
|
if ( IsPropProxyValid( iProp ) ) |
|
{ |
|
AddProp( iProp ); |
|
|
|
// avoid that we add it twice. |
|
if ( *pCurNewProxyProp == iProp ) |
|
++pCurNewProxyProp; |
|
} |
|
} |
|
|
|
// add any remaining new proxy props |
|
while ( (unsigned int) *pCurNewProxyProp < MAX_DATATABLE_PROPS ) |
|
{ |
|
AddProp( *pCurNewProxyProp ); |
|
++pCurNewProxyProp; |
|
} |
|
} |
|
|
|
int GetNumOutProps() |
|
{ |
|
return m_nOutProps; |
|
} |
|
|
|
|
|
private: |
|
CSendTablePrecalc *m_pPrecalc; |
|
int m_iClient; // Which client it's encoding out for. |
|
const CSendProxyRecipients *m_pOldStateProxies; |
|
const int m_nOldStateProxies; |
|
|
|
const CSendProxyRecipients *m_pNewStateProxies; |
|
const int m_nNewStateProxies; |
|
|
|
// The output property list. |
|
int *m_pOutProps; |
|
int m_nMaxOutProps; |
|
int m_nOutProps; |
|
|
|
int m_NewProxyProps[MAX_DATATABLE_PROPS+1]; |
|
int m_nNewProxyProps; |
|
}; |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------- // |
|
// CEncodeInfo |
|
// Used by SendTable_Encode. |
|
// ----------------------------------------------------------------------------- // |
|
class CEncodeInfo : public CServerDatatableStack |
|
{ |
|
public: |
|
CEncodeInfo( CSendTablePrecalc *pPrecalc, unsigned char *pStructBase, int objectID, bf_write *pOut ) : |
|
CServerDatatableStack( pPrecalc, pStructBase, objectID ), |
|
m_DeltaBitsWriter( pOut ) |
|
{ |
|
} |
|
|
|
public: |
|
CDeltaBitsWriter m_DeltaBitsWriter; |
|
}; |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------ // |
|
// Globals. |
|
// ------------------------------------------------------------------------ // |
|
|
|
CUtlVector< SendTable* > g_SendTables; |
|
CRC32_t g_SendTableCRC = 0; |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------ // |
|
// SendTable functions. |
|
// ------------------------------------------------------------------------ // |
|
|
|
static bool s_debug_info_shown = false; |
|
static int s_debug_bits_start = 0; |
|
|
|
|
|
static inline void ShowEncodeDeltaWatchInfo( |
|
const SendTable *pTable, |
|
const SendProp *pProp, |
|
bf_read &buffer, |
|
const int objectID, |
|
const int index ) |
|
{ |
|
if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) ) |
|
return; |
|
|
|
static int lastframe = -1; |
|
if ( host_framecount != lastframe ) |
|
{ |
|
lastframe = host_framecount; |
|
ConDMsg( "delta entity: %i\n", objectID ); |
|
} |
|
|
|
// work on copy of bitbuffer |
|
bf_read copy = buffer; |
|
|
|
s_debug_info_shown = true; |
|
|
|
DecodeInfo info; |
|
info.m_pStruct = NULL; |
|
info.m_pData = NULL; |
|
info.m_pRecvProp = NULL; |
|
info.m_pProp = pProp; |
|
info.m_pIn = © |
|
info.m_Value.m_Type = (SendPropType)pProp->m_Type; |
|
|
|
int startBit = copy.GetNumBitsRead(); |
|
|
|
g_PropTypeFns[pProp->m_Type].Decode( &info ); |
|
|
|
int bits = copy.GetNumBitsRead() - startBit; |
|
|
|
const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString(); |
|
const char *value = info.m_Value.ToString(); |
|
|
|
ConDMsg( "+ %s %s, %s, index %i, bits %i, value %s\n", pTable->GetName(), pProp->GetName(), type, index, bits, value ); |
|
} |
|
|
|
|
|
static FORCEINLINE void SendTable_EncodeProp( CEncodeInfo * pInfo, unsigned long iProp ) |
|
{ |
|
// Call their proxy to get the property's value. |
|
DVariant var; |
|
|
|
const SendProp *pProp = pInfo->GetCurProp(); |
|
unsigned char *pStructBase = pInfo->GetCurStructBase(); |
|
|
|
pProp->GetProxyFn()( |
|
pProp, |
|
pStructBase, |
|
pStructBase + pProp->GetOffset(), |
|
&var, |
|
0, // iElement |
|
pInfo->GetObjectID() |
|
); |
|
|
|
// Write the index. |
|
pInfo->m_DeltaBitsWriter.WritePropIndex( iProp ); |
|
|
|
g_PropTypeFns[pProp->m_Type].Encode( |
|
pStructBase, |
|
&var, |
|
pProp, |
|
pInfo->m_DeltaBitsWriter.GetBitBuf(), |
|
pInfo->GetObjectID() |
|
); |
|
} |
|
|
|
|
|
static bool SendTable_IsPropZero( CEncodeInfo *pInfo, unsigned long iProp ) |
|
{ |
|
const SendProp *pProp = pInfo->GetCurProp(); |
|
|
|
// Call their proxy to get the property's value. |
|
DVariant var; |
|
unsigned char *pBase = pInfo->GetCurStructBase(); |
|
|
|
pProp->GetProxyFn()( |
|
pProp, |
|
pBase, |
|
pBase + pProp->GetOffset(), |
|
&var, |
|
0, // iElement |
|
pInfo->GetObjectID() |
|
); |
|
|
|
return g_PropTypeFns[pProp->m_Type].IsZero( pBase, &var, pProp ); |
|
} |
|
|
|
|
|
int SendTable_CullPropsFromProxies( |
|
const SendTable *pTable, |
|
|
|
const int *pStartProps, |
|
int nStartProps, |
|
|
|
const int iClient, |
|
|
|
const CSendProxyRecipients *pOldStateProxies, |
|
const int nOldStateProxies, |
|
|
|
const CSendProxyRecipients *pNewStateProxies, |
|
const int nNewStateProxies, |
|
|
|
int *pOutProps, |
|
int nMaxOutProps |
|
) |
|
{ |
|
Assert( !( nNewStateProxies && !pNewStateProxies ) ); |
|
CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies ); |
|
|
|
stack.CullPropsFromProxies( pStartProps, nStartProps, pOutProps, nMaxOutProps ); |
|
|
|
ErrorIfNot( stack.GetNumOutProps() <= nMaxOutProps, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) ); |
|
return stack.GetNumOutProps(); |
|
} |
|
|
|
|
|
// compares properties and writes delta properties, it ignores reciepients |
|
int SendTable_WriteAllDeltaProps( |
|
const SendTable *pTable, |
|
const void *pFromData, |
|
const int nFromDataBits, |
|
const void *pToData, |
|
const int nToDataBits, |
|
const int nObjectID, |
|
bf_write *pBufOut ) |
|
{ |
|
// Calculate the delta props. |
|
int deltaProps[MAX_DATATABLE_PROPS]; |
|
|
|
int nDeltaProps = SendTable_CalcDelta( |
|
pTable, |
|
pFromData, |
|
nFromDataBits, |
|
pToData, |
|
nToDataBits, |
|
deltaProps, |
|
ARRAYSIZE( deltaProps ), |
|
nObjectID ); |
|
|
|
// Write the properties. |
|
SendTable_WritePropList( |
|
pTable, |
|
pToData, // object data |
|
nToDataBits, |
|
pBufOut, // output buffer |
|
nObjectID, |
|
deltaProps, |
|
nDeltaProps |
|
); |
|
|
|
return nDeltaProps; |
|
} |
|
|
|
|
|
bool SendTable_Encode( |
|
const SendTable *pTable, |
|
const void *pStruct, |
|
bf_write *pOut, |
|
int objectID, |
|
CUtlMemory<CSendProxyRecipients> *pRecipients, |
|
bool bNonZeroOnly |
|
) |
|
{ |
|
CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc; |
|
ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) ); |
|
if ( pRecipients ) |
|
{ |
|
ErrorIfNot( pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") ); |
|
} |
|
|
|
VPROF( "SendTable_Encode" ); |
|
|
|
CServerDTITimer timer( pTable, SERVERDTI_ENCODE ); |
|
|
|
// Setup all the info we'll be walking the tree with. |
|
CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID, pOut ); |
|
info.m_pRecipients = pRecipients; // optional buffer to store the bits for which clients get what data. |
|
|
|
info.Init(); |
|
|
|
int iNumProps = pPrecalc->GetNumProps(); |
|
|
|
for ( int iProp=0; iProp < iNumProps; iProp++ ) |
|
{ |
|
// skip if we don't have a valid prop proxy |
|
if ( !info.IsPropProxyValid( iProp ) ) |
|
continue; |
|
|
|
info.SeekToProp( iProp ); |
|
|
|
// skip empty prop if we only encode non-zero values |
|
if ( bNonZeroOnly && SendTable_IsPropZero(&info, iProp) ) |
|
continue; |
|
|
|
SendTable_EncodeProp( &info, iProp ); |
|
} |
|
|
|
return !pOut->IsOverflowed(); |
|
} |
|
|
|
|
|
void SendTable_WritePropList( |
|
const SendTable *pTable, |
|
const void *pState, |
|
const int nBits, |
|
bf_write *pOut, |
|
const int objectID, |
|
const int *pCheckProps, |
|
const int nCheckProps |
|
) |
|
{ |
|
if ( nCheckProps == 0 ) |
|
{ |
|
// Write single final zero bit, signifying that there no changed properties |
|
pOut->WriteOneBit( 0 ); |
|
return; |
|
} |
|
|
|
bool bDebugWatch = Sendprop_UsingDebugWatch(); |
|
|
|
s_debug_info_shown = false; |
|
s_debug_bits_start = pOut->GetNumBitsWritten(); |
|
|
|
CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc; |
|
CDeltaBitsWriter deltaBitsWriter( pOut ); |
|
|
|
bf_read inputBuffer( "SendTable_WritePropList->inputBuffer", pState, BitByte( nBits ), nBits ); |
|
CDeltaBitsReader inputBitsReader( &inputBuffer ); |
|
|
|
// Ok, they want to specify a small list of properties to check. |
|
unsigned int iToProp = inputBitsReader.ReadNextPropIndex(); |
|
int i = 0; |
|
while ( i < nCheckProps ) |
|
{ |
|
// Seek the 'to' state to the current property we want to check. |
|
while ( iToProp < (unsigned int) pCheckProps[i] ) |
|
{ |
|
inputBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) ); |
|
iToProp = inputBitsReader.ReadNextPropIndex(); |
|
} |
|
|
|
if ( iToProp >= MAX_DATATABLE_PROPS ) |
|
{ |
|
break; |
|
} |
|
|
|
if ( iToProp == (unsigned int) pCheckProps[i] ) |
|
{ |
|
const SendProp *pProp = pPrecalc->GetProp( iToProp ); |
|
|
|
// Show debug stuff. |
|
if ( bDebugWatch ) |
|
{ |
|
ShowEncodeDeltaWatchInfo( pTable, pProp, inputBuffer, objectID, iToProp ); |
|
} |
|
|
|
// See how many bits the data for this property takes up. |
|
int nToStateBits; |
|
int iStartBit = pOut->GetNumBitsWritten(); |
|
|
|
deltaBitsWriter.WritePropIndex( iToProp ); |
|
inputBitsReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pProp ); |
|
|
|
nToStateBits = pOut->GetNumBitsWritten() - iStartBit; |
|
|
|
TRACE_PACKET( ( " Send Field (%s) = %d (%d bytes)\n", pProp->GetName(), nToStateBits, ( nToStateBits + 7 ) / 8 ) ); |
|
|
|
// Seek to the next prop. |
|
iToProp = inputBitsReader.ReadNextPropIndex(); |
|
} |
|
|
|
++i; |
|
} |
|
|
|
if ( s_debug_info_shown ) |
|
{ |
|
int bits = pOut->GetNumBitsWritten() - s_debug_bits_start; |
|
ConDMsg( "= %i bits (%i bytes)\n", bits, Bits2Bytes(bits) ); |
|
} |
|
|
|
inputBitsReader.ForceFinished(); // avoid a benign assert |
|
} |
|
|
|
|
|
int SendTable_CalcDelta( |
|
const SendTable *pTable, |
|
|
|
const void *pFromState, |
|
const int nFromBits, |
|
|
|
const void *pToState, |
|
const int nToBits, |
|
|
|
int *pDeltaProps, |
|
int nMaxDeltaProps, |
|
|
|
const int objectID |
|
) |
|
{ |
|
CServerDTITimer timer( pTable, SERVERDTI_CALCDELTA ); |
|
|
|
int *pDeltaPropsBase = pDeltaProps; |
|
int *pDeltaPropsEnd = pDeltaProps + nMaxDeltaProps; |
|
|
|
VPROF( "SendTable_CalcDelta" ); |
|
|
|
// Trivial reject. |
|
//if ( CompareBitArrays( pFromState, pToState, nFromBits, nToBits ) ) |
|
//{ |
|
// return 0; |
|
//} |
|
|
|
CSendTablePrecalc* pPrecalc = pTable->m_pPrecalc; |
|
|
|
bf_read toBits( "SendTable_CalcDelta/toBits", pToState, BitByte(nToBits), nToBits ); |
|
CDeltaBitsReader toBitsReader( &toBits ); |
|
unsigned int iToProp = toBitsReader.ReadNextPropIndex(); |
|
|
|
if ( pFromState ) |
|
{ |
|
bf_read fromBits( "SendTable_CalcDelta/fromBits", pFromState, BitByte(nFromBits), nFromBits ); |
|
CDeltaBitsReader fromBitsReader( &fromBits ); |
|
unsigned int iFromProp = fromBitsReader.ReadNextPropIndex(); |
|
|
|
for ( ; iToProp < MAX_DATATABLE_PROPS; iToProp = toBitsReader.ReadNextPropIndex() ) |
|
{ |
|
Assert( (int)iToProp >= 0 ); |
|
|
|
// Skip any properties in the from state that aren't in the to state. |
|
while ( iFromProp < iToProp ) |
|
{ |
|
fromBitsReader.SkipPropData( pPrecalc->GetProp( iFromProp ) ); |
|
iFromProp = fromBitsReader.ReadNextPropIndex(); |
|
} |
|
|
|
if ( iFromProp == iToProp ) |
|
{ |
|
// The property is in both states, so compare them and write the index |
|
// if the states are different. |
|
if ( fromBitsReader.ComparePropData( &toBitsReader, pPrecalc->GetProp( iToProp ) ) ) |
|
{ |
|
*pDeltaProps++ = iToProp; |
|
if ( pDeltaProps >= pDeltaPropsEnd ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
// Seek to the next property. |
|
iFromProp = fromBitsReader.ReadNextPropIndex(); |
|
} |
|
else |
|
{ |
|
// Only the 'to' state has this property, so just skip its data and register a change. |
|
toBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) ); |
|
*pDeltaProps++ = iToProp; |
|
if ( pDeltaProps >= pDeltaPropsEnd ) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
Assert( iToProp == ~0u ); |
|
|
|
fromBitsReader.ForceFinished(); |
|
} |
|
else |
|
{ |
|
for ( ; iToProp != (uint)-1; iToProp = toBitsReader.ReadNextPropIndex() ) |
|
{ |
|
Assert( (int)iToProp >= 0 && iToProp < MAX_DATATABLE_PROPS ); |
|
|
|
const SendProp *pProp = pPrecalc->GetProp( iToProp ); |
|
if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &toBits ) ) |
|
{ |
|
*pDeltaProps++ = iToProp; |
|
if ( pDeltaProps >= pDeltaPropsEnd ) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Return the # of properties that changed between 'from' and 'to'. |
|
return pDeltaProps - pDeltaPropsBase; |
|
} |
|
|
|
bool SendTable_WriteInfos( SendTable *pTable, bf_write *pBuf ) |
|
{ |
|
pBuf->WriteString( pTable->GetName() ); |
|
pBuf->WriteUBitLong( pTable->GetNumProps(), PROPINFOBITS_NUMPROPS ); |
|
|
|
// Send each property. |
|
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) |
|
{ |
|
const SendProp *pProp = &pTable->m_pProps[iProp]; |
|
|
|
pBuf->WriteUBitLong( (unsigned int)pProp->m_Type, PROPINFOBITS_TYPE ); |
|
pBuf->WriteString( pProp->GetName() ); |
|
// we now have some flags that aren't networked so strip them off |
|
unsigned int networkFlags = pProp->GetFlags() & ((1<<PROPINFOBITS_FLAGS)-1); |
|
pBuf->WriteUBitLong( networkFlags, PROPINFOBITS_FLAGS ); |
|
|
|
if( pProp->m_Type == DPT_DataTable ) |
|
{ |
|
// Just write the name and it will be able to reuse the table with a matching name. |
|
pBuf->WriteString( pProp->GetDataTable()->m_pNetTableName ); |
|
} |
|
else |
|
{ |
|
if ( pProp->IsExcludeProp() ) |
|
{ |
|
pBuf->WriteString( pProp->GetExcludeDTName() ); |
|
} |
|
else if ( pProp->GetType() == DPT_Array ) |
|
{ |
|
pBuf->WriteUBitLong( pProp->GetNumElements(), PROPINFOBITS_NUMELEMENTS ); |
|
} |
|
else |
|
{ |
|
pBuf->WriteBitFloat( pProp->m_fLowValue ); |
|
pBuf->WriteBitFloat( pProp->m_fHighValue ); |
|
pBuf->WriteUBitLong( pProp->m_nBits, PROPINFOBITS_NUMBITS ); |
|
} |
|
} |
|
} |
|
|
|
return !pBuf->IsOverflowed(); |
|
} |
|
|
|
|
|
|
|
// Spits out warnings for invalid properties and forces property values to |
|
// be in valid ranges for the encoders and decoders. |
|
static void SendTable_Validate( CSendTablePrecalc *pPrecalc ) |
|
{ |
|
SendTable *pTable = pPrecalc->m_pSendTable; |
|
for( int i=0; i < pTable->m_nProps; i++ ) |
|
{ |
|
SendProp *pProp = &pTable->m_pProps[i]; |
|
|
|
if ( pProp->GetArrayProp() ) |
|
{ |
|
if ( pProp->GetArrayProp()->GetType() == DPT_DataTable ) |
|
{ |
|
Error( "Invalid property: %s/%s (array of datatables) [on prop %d of %d (%s)].", pTable->m_pNetTableName, pProp->GetName(), i, pTable->m_nProps, pProp->GetArrayProp()->GetName() ); |
|
} |
|
} |
|
else |
|
{ |
|
ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) ); |
|
} |
|
|
|
// Check for 1-bit signed properties (their value doesn't get down to the client). |
|
if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) ) |
|
{ |
|
DataTable_Warning("SendTable prop %s::%s is a 1-bit signed property. Use SPROP_UNSIGNED or the client will never receive a value.\n", pTable->m_pNetTableName, pProp->GetName()); |
|
} |
|
} |
|
|
|
for ( int i = 0; i < pPrecalc->GetNumProps(); ++i ) |
|
{ |
|
const SendProp *pProp = pPrecalc->GetProp( i ); |
|
if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT ) |
|
{ |
|
pTable->SetHasPropsEncodedAgainstTickcount( true ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
static void SendTable_CalcNextVectorElems( SendTable *pTable ) |
|
{ |
|
for ( int i=0; i < pTable->GetNumProps(); i++ ) |
|
{ |
|
SendProp *pProp = pTable->GetProp( i ); |
|
|
|
if ( pProp->GetType() == DPT_DataTable ) |
|
{ |
|
SendTable_CalcNextVectorElems( pProp->GetDataTable() ); |
|
} |
|
else if ( pProp->GetOffset() < 0 ) |
|
{ |
|
pProp->SetOffset( -pProp->GetOffset() ); |
|
pProp->SetFlags( pProp->GetFlags() | SPROP_IS_A_VECTOR_ELEM ); |
|
} |
|
} |
|
} |
|
|
|
|
|
static bool SendTable_InitTable( SendTable *pTable ) |
|
{ |
|
if( pTable->m_pPrecalc ) |
|
return true; |
|
|
|
// Create the CSendTablePrecalc. |
|
CSendTablePrecalc *pPrecalc = new CSendTablePrecalc; |
|
pTable->m_pPrecalc = pPrecalc; |
|
|
|
pPrecalc->m_pSendTable = pTable; |
|
pTable->m_pPrecalc = pPrecalc; |
|
|
|
SendTable_CalcNextVectorElems( pTable ); |
|
|
|
// Bind the instrumentation if -dti was specified. |
|
pPrecalc->m_pDTITable = ServerDTI_HookTable( pTable ); |
|
|
|
// Setup its flat property array. |
|
if ( !pPrecalc->SetupFlatPropertyArray() ) |
|
return false; |
|
|
|
SendTable_Validate( pPrecalc ); |
|
return true; |
|
} |
|
|
|
|
|
static void SendTable_TermTable( SendTable *pTable ) |
|
{ |
|
if( !pTable->m_pPrecalc ) |
|
return; |
|
|
|
delete pTable->m_pPrecalc; |
|
Assert( !pTable->m_pPrecalc ); // Make sure it unbound itself. |
|
} |
|
|
|
|
|
int SendTable_GetNumFlatProps( SendTable *pSendTable ) |
|
{ |
|
CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc; |
|
ErrorIfNot( pPrecalc, |
|
("SendTable_GetNumFlatProps: missing pPrecalc.") |
|
); |
|
return pPrecalc->GetNumProps(); |
|
} |
|
|
|
CRC32_t SendTable_CRCTable( CRC32_t &crc, SendTable *pTable ) |
|
{ |
|
CRC32_ProcessBuffer( &crc, (void *)pTable->m_pNetTableName, Q_strlen( pTable->m_pNetTableName) ); |
|
|
|
int nProps = LittleLong( pTable->m_nProps ); |
|
CRC32_ProcessBuffer( &crc, (void *)&nProps, sizeof( pTable->m_nProps ) ); |
|
|
|
// Send each property. |
|
for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) |
|
{ |
|
const SendProp *pProp = &pTable->m_pProps[iProp]; |
|
|
|
int type = LittleLong( pProp->m_Type ); |
|
CRC32_ProcessBuffer( &crc, (void *)&type, sizeof( type ) ); |
|
CRC32_ProcessBuffer( &crc, (void *)pProp->GetName() , Q_strlen( pProp->GetName() ) ); |
|
|
|
int flags = LittleLong( pProp->GetFlags() ); |
|
CRC32_ProcessBuffer( &crc, (void *)&flags, sizeof( flags ) ); |
|
|
|
if( pProp->m_Type == DPT_DataTable ) |
|
{ |
|
CRC32_ProcessBuffer( &crc, (void *)pProp->GetDataTable()->m_pNetTableName, Q_strlen( pProp->GetDataTable()->m_pNetTableName ) ); |
|
} |
|
else |
|
{ |
|
if ( pProp->IsExcludeProp() ) |
|
{ |
|
CRC32_ProcessBuffer( &crc, (void *)pProp->GetExcludeDTName(), Q_strlen( pProp->GetExcludeDTName() ) ); |
|
} |
|
else if ( pProp->GetType() == DPT_Array ) |
|
{ |
|
int numelements = LittleLong( pProp->GetNumElements() ); |
|
CRC32_ProcessBuffer( &crc, (void *)&numelements, sizeof( numelements ) ); |
|
} |
|
else |
|
{ |
|
float lowvalue; |
|
LittleFloat( &lowvalue, &pProp->m_fLowValue ); |
|
CRC32_ProcessBuffer( &crc, (void *)&lowvalue, sizeof( lowvalue ) ); |
|
|
|
float highvalue; |
|
LittleFloat( &highvalue, &pProp->m_fHighValue ); |
|
CRC32_ProcessBuffer( &crc, (void *)&highvalue, sizeof( highvalue ) ); |
|
|
|
int bits = LittleLong( pProp->m_nBits ); |
|
CRC32_ProcessBuffer( &crc, (void *)&bits, sizeof( bits ) ); |
|
} |
|
} |
|
} |
|
|
|
return crc; |
|
} |
|
|
|
void SendTable_PrintStats( void ) |
|
{ |
|
int numTables = 0; |
|
int numFloats = 0; |
|
int numStrings = 0; |
|
int numArrays = 0; |
|
int numInts = 0; |
|
int numVecs = 0; |
|
int numVecXYs = 0; |
|
int numSubTables = 0; |
|
int numSendProps = 0; |
|
int numFlatProps = 0; |
|
int numExcludeProps = 0; |
|
|
|
for ( int i=0; i < g_SendTables.Count(); i++ ) |
|
{ |
|
SendTable *st = g_SendTables[i]; |
|
|
|
numTables++; |
|
numSendProps += st->GetNumProps(); |
|
numFlatProps += st->m_pPrecalc->GetNumProps(); |
|
|
|
for ( int j=0; j < st->GetNumProps(); j++ ) |
|
{ |
|
SendProp* sp = st->GetProp( j ); |
|
|
|
if ( sp->IsExcludeProp() ) |
|
{ |
|
numExcludeProps++; |
|
continue; // no real sendprops |
|
} |
|
|
|
if ( sp->IsInsideArray() ) |
|
continue; |
|
|
|
switch( sp->GetType() ) |
|
{ |
|
case DPT_Int : numInts++; break; |
|
case DPT_Float : numFloats++; break; |
|
case DPT_Vector : numVecs++; break; |
|
case DPT_VectorXY : numVecXYs++; break; |
|
case DPT_String : numStrings++; break; |
|
case DPT_Array : numArrays++; break; |
|
case DPT_DataTable : numSubTables++; break; |
|
} |
|
} |
|
} |
|
|
|
Msg("Total Send Table stats\n"); |
|
Msg("Send Tables : %i\n", numTables ); |
|
Msg("Send Props : %i\n", numSendProps ); |
|
Msg("Flat Props : %i\n", numFlatProps ); |
|
Msg("Int Props : %i\n", numInts ); |
|
Msg("Float Props : %i\n", numFloats ); |
|
Msg("Vector Props : %i\n", numVecs ); |
|
Msg("VectorXY Props: %i\n", numVecXYs ); |
|
Msg("String Props : %i\n", numStrings ); |
|
Msg("Array Props : %i\n", numArrays ); |
|
Msg("Table Props : %i\n", numSubTables ); |
|
Msg("Exclu Props : %i\n", numExcludeProps ); |
|
} |
|
|
|
|
|
|
|
bool SendTable_Init( SendTable **pTables, int nTables ) |
|
{ |
|
ErrorIfNot( g_SendTables.Count() == 0, |
|
("SendTable_Init: called twice.") |
|
); |
|
|
|
// Initialize them all. |
|
for ( int i=0; i < nTables; i++ ) |
|
{ |
|
if ( !SendTable_InitTable( pTables[i] ) ) |
|
return false; |
|
} |
|
|
|
// Store off the SendTable list. |
|
g_SendTables.CopyArray( pTables, nTables ); |
|
|
|
g_SendTableCRC = SendTable_ComputeCRC( ); |
|
|
|
if ( CommandLine()->FindParm("-dti" ) ) |
|
{ |
|
SendTable_PrintStats(); |
|
} |
|
|
|
return true; |
|
} |
|
void SendTable_Term() |
|
{ |
|
// Term all the SendTables. |
|
for ( int i=0; i < g_SendTables.Count(); i++ ) |
|
SendTable_TermTable( g_SendTables[i] ); |
|
|
|
// Clear the list of SendTables. |
|
g_SendTables.Purge(); |
|
g_SendTableCRC = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Computes the crc for all sendtables for the data sent in the class/table definitions |
|
// Output : CRC32_t |
|
//----------------------------------------------------------------------------- |
|
CRC32_t SendTable_ComputeCRC() |
|
{ |
|
CRC32_t result; |
|
CRC32_Init( &result ); |
|
|
|
// walk the tables and checksum them |
|
int c = g_SendTables.Count(); |
|
for ( int i = 0 ; i < c; i++ ) |
|
{ |
|
SendTable *st = g_SendTables[ i ]; |
|
result = SendTable_CRCTable( result, st ); |
|
} |
|
|
|
|
|
CRC32_Final( &result ); |
|
|
|
return result; |
|
} |
|
|
|
SendTable *SendTabe_GetTable(int index) |
|
{ |
|
return g_SendTables[index]; |
|
} |
|
|
|
int SendTable_GetNum() |
|
{ |
|
return g_SendTables.Count(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : CRC32_t |
|
//----------------------------------------------------------------------------- |
|
CRC32_t SendTable_GetCRC() |
|
{ |
|
return g_SendTableCRC; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: check integrity of an unpacked entity send table |
|
//----------------------------------------------------------------------------- |
|
bool SendTable_CheckIntegrity( SendTable *pTable, const void *pData, const int nDataBits ) |
|
{ |
|
#ifdef _DEBUG |
|
if ( pData == NULL && nDataBits == 0 ) |
|
return true; |
|
|
|
bf_read bfRead( "SendTable_CheckIntegrity", pData, Bits2Bytes(nDataBits), nDataBits ); |
|
CDeltaBitsReader bitsReader( &bfRead ); |
|
|
|
int iProp = -1; |
|
int iLastProp = -1; |
|
int nMaxProps = pTable->m_pPrecalc->GetNumProps(); |
|
int nPropCount = 0; |
|
|
|
Assert( nMaxProps > 0 && nMaxProps < MAX_DATATABLE_PROPS ); |
|
|
|
while( -1 != (iProp = bitsReader.ReadNextPropIndex()) ) |
|
{ |
|
Assert( (iProp>=0) && (iProp<nMaxProps) ); |
|
|
|
// must be larger |
|
Assert( iProp > iLastProp ); |
|
|
|
const SendProp *pProp = pTable->m_pPrecalc->GetProp( iProp ); |
|
|
|
Assert( pProp ); |
|
|
|
// ok check that SkipProp & IsEncodedZero read the same bit length |
|
int iStartBit = bfRead.GetNumBitsRead(); |
|
g_PropTypeFns[ pProp->GetType() ].SkipProp( pProp, &bfRead ); |
|
int nLength = bfRead.GetNumBitsRead() - iStartBit; |
|
|
|
Assert( nLength > 0 ); // a prop must have some bits |
|
|
|
bfRead.Seek( iStartBit ); // read it again |
|
|
|
g_PropTypeFns[ pProp->GetType() ].IsEncodedZero( pProp, &bfRead ); |
|
|
|
Assert( nLength == (bfRead.GetNumBitsRead() - iStartBit) ); |
|
|
|
nPropCount++; |
|
iLastProp = iProp; |
|
} |
|
|
|
Assert( nPropCount <= nMaxProps ); |
|
Assert( bfRead.GetNumBytesLeft() < 4 ); |
|
Assert( !bfRead.IsOverflowed() ); |
|
|
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|