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.
567 lines
14 KiB
567 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include <stdarg.h> |
|
#include "dt_send.h" |
|
#include "dt.h" |
|
#include "dt_recv.h" |
|
#include "dt_encode.h" |
|
#include "convar.h" |
|
#include "commonmacros.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/dbg.h" |
|
#include "dt_stack.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
#define PROPINDEX_NUMBITS 12 |
|
#define MAX_TOTAL_SENDTABLE_PROPS (1 << PROPINDEX_NUMBITS) |
|
|
|
|
|
ConVar g_CV_DTWatchEnt( "dtwatchent", "-1", 0, "Watch this entities data table encoding." ); |
|
ConVar g_CV_DTWatchVar( "dtwatchvar", "", 0, "Watch the named variable." ); |
|
ConVar g_CV_DTWarning( "dtwarning", "0", 0, "Print data table warnings?" ); |
|
ConVar g_CV_DTWatchClass( "dtwatchclass", "", 0, "Watch all fields encoded with this table." ); |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------- // |
|
// |
|
// CBuildHierarchyStruct |
|
// |
|
// Used while building a CSendNode hierarchy. |
|
// |
|
// ----------------------------------------------------------------------------- // |
|
class CBuildHierarchyStruct |
|
{ |
|
public: |
|
const ExcludeProp *m_pExcludeProps; |
|
int m_nExcludeProps; |
|
|
|
const SendProp *m_pDatatableProps[MAX_TOTAL_SENDTABLE_PROPS]; |
|
int m_nDatatableProps; |
|
|
|
const SendProp *m_pProps[MAX_TOTAL_SENDTABLE_PROPS]; |
|
unsigned char m_PropProxyIndices[MAX_TOTAL_SENDTABLE_PROPS]; |
|
int m_nProps; |
|
|
|
unsigned char m_nPropProxies; |
|
}; |
|
|
|
|
|
// ----------------------------------------------------------------------------- // |
|
// CSendNode. |
|
// ----------------------------------------------------------------------------- // |
|
|
|
CSendNode::CSendNode() |
|
{ |
|
m_iDatatableProp = -1; |
|
m_pTable = NULL; |
|
|
|
m_iFirstRecursiveProp = m_nRecursiveProps = 0; |
|
|
|
m_DataTableProxyIndex = DATATABLE_PROXY_INDEX_INVALID; // set it to a questionable value. |
|
} |
|
|
|
CSendNode::~CSendNode() |
|
{ |
|
int c = GetNumChildren(); |
|
for ( int i = c - 1 ; i >= 0 ; i-- ) |
|
{ |
|
delete GetChild( i ); |
|
} |
|
m_Children.Purge(); |
|
} |
|
|
|
// ----------------------------------------------------------------------------- // |
|
// CSendTablePrecalc |
|
// ----------------------------------------------------------------------------- // |
|
|
|
bool PropOffsetLT( const unsigned short &a, const unsigned short &b ) |
|
{ |
|
return a < b; |
|
} |
|
|
|
CSendTablePrecalc::CSendTablePrecalc() : |
|
m_PropOffsetToIndexMap( 0, 0, PropOffsetLT ) |
|
{ |
|
m_pDTITable = NULL; |
|
m_pSendTable = 0; |
|
m_nDataTableProxies = 0; |
|
} |
|
|
|
|
|
CSendTablePrecalc::~CSendTablePrecalc() |
|
{ |
|
if ( m_pSendTable ) |
|
m_pSendTable->m_pPrecalc = 0; |
|
} |
|
|
|
|
|
const ExcludeProp* FindExcludeProp( |
|
char const *pTableName, |
|
char const *pPropName, |
|
const ExcludeProp *pExcludeProps, |
|
int nExcludeProps) |
|
{ |
|
for ( int i=0; i < nExcludeProps; i++ ) |
|
{ |
|
if ( stricmp(pExcludeProps[i].m_pTableName, pTableName) == 0 && stricmp(pExcludeProps[i].m_pPropName, pPropName ) == 0 ) |
|
return &pExcludeProps[i]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
// Fill in a list of all the excluded props. |
|
static bool SendTable_GetPropsExcluded( const SendTable *pTable, ExcludeProp *pExcludeProps, int &nExcludeProps, int nMaxExcludeProps ) |
|
{ |
|
for(int i=0; i < pTable->m_nProps; i++) |
|
{ |
|
SendProp *pProp = &pTable->m_pProps[i]; |
|
|
|
if ( pProp->IsExcludeProp() ) |
|
{ |
|
char const *pName = pProp->GetExcludeDTName(); |
|
|
|
ErrorIfNot( pName, |
|
("Found an exclude prop missing a name.") |
|
); |
|
|
|
ErrorIfNot( nExcludeProps < nMaxExcludeProps, |
|
("SendTable_GetPropsExcluded: Overflowed max exclude props with %s.", pName) |
|
); |
|
|
|
pExcludeProps[nExcludeProps].m_pTableName = pName; |
|
pExcludeProps[nExcludeProps].m_pPropName = pProp->GetName(); |
|
nExcludeProps++; |
|
} |
|
else if ( pProp->GetDataTable() ) |
|
{ |
|
if( !SendTable_GetPropsExcluded( pProp->GetDataTable(), pExcludeProps, nExcludeProps, nMaxExcludeProps ) ) |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
// Set the datatable proxy indices in all datatable SendProps. |
|
static void SetDataTableProxyIndices_R( |
|
CSendTablePrecalc *pMainTable, |
|
CSendNode *pCurTable, |
|
CBuildHierarchyStruct *bhs ) |
|
{ |
|
for ( int i=0; i < pCurTable->GetNumChildren(); i++ ) |
|
{ |
|
CSendNode *pNode = pCurTable->GetChild( i ); |
|
const SendProp *pProp = bhs->m_pDatatableProps[pNode->m_iDatatableProp]; |
|
|
|
if ( pProp->GetFlags() & SPROP_PROXY_ALWAYS_YES ) |
|
{ |
|
pNode->SetDataTableProxyIndex( DATATABLE_PROXY_INDEX_NOPROXY ); |
|
} |
|
else |
|
{ |
|
pNode->SetDataTableProxyIndex( pMainTable->GetNumDataTableProxies() ); |
|
pMainTable->SetNumDataTableProxies( pMainTable->GetNumDataTableProxies() + 1 ); |
|
} |
|
|
|
SetDataTableProxyIndices_R( pMainTable, pNode, bhs ); |
|
} |
|
} |
|
|
|
// Set the datatable proxy indices in all datatable SendProps. |
|
static void SetRecursiveProxyIndices_R( |
|
SendTable *pBaseTable, |
|
CSendNode *pCurTable, |
|
int &iCurProxyIndex ) |
|
{ |
|
if ( iCurProxyIndex >= CDatatableStack::MAX_PROXY_RESULTS ) |
|
Error( "Too many proxies for datatable %s.", pBaseTable->GetName() ); |
|
|
|
pCurTable->SetRecursiveProxyIndex( iCurProxyIndex ); |
|
iCurProxyIndex++; |
|
|
|
for ( int i=0; i < pCurTable->GetNumChildren(); i++ ) |
|
{ |
|
CSendNode *pNode = pCurTable->GetChild( i ); |
|
SetRecursiveProxyIndices_R( pBaseTable, pNode, iCurProxyIndex ); |
|
} |
|
} |
|
|
|
|
|
void SendTable_BuildHierarchy( |
|
CSendNode *pNode, |
|
const SendTable *pTable, |
|
CBuildHierarchyStruct *bhs |
|
); |
|
|
|
|
|
void SendTable_BuildHierarchy_IterateProps( |
|
CSendNode *pNode, |
|
const SendTable *pTable, |
|
CBuildHierarchyStruct *bhs, |
|
const SendProp *pNonDatatableProps[MAX_TOTAL_SENDTABLE_PROPS], |
|
int &nNonDatatableProps ) |
|
{ |
|
int i; |
|
for ( i=0; i < pTable->m_nProps; i++ ) |
|
{ |
|
const SendProp *pProp = &pTable->m_pProps[i]; |
|
|
|
if ( pProp->IsExcludeProp() || |
|
pProp->IsInsideArray() || |
|
FindExcludeProp( pTable->GetName(), pProp->GetName(), bhs->m_pExcludeProps, bhs->m_nExcludeProps ) ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( pProp->GetType() == DPT_DataTable ) |
|
{ |
|
if ( pProp->GetFlags() & SPROP_COLLAPSIBLE ) |
|
{ |
|
// This is a base class.. no need to make a new CSendNode (and trigger a bunch of |
|
// unnecessary send proxy calls in the datatable stacks). |
|
SendTable_BuildHierarchy_IterateProps( |
|
pNode, |
|
pProp->GetDataTable(), |
|
bhs, |
|
pNonDatatableProps, |
|
nNonDatatableProps ); |
|
} |
|
else |
|
{ |
|
// Setup a child datatable reference. |
|
CSendNode *pChild = new CSendNode; |
|
|
|
// Setup a datatable prop for this node to reference (so the recursion |
|
// routines can get at the proxy). |
|
if ( bhs->m_nDatatableProps >= ARRAYSIZE( bhs->m_pDatatableProps ) ) |
|
Error( "Overflowed datatable prop list in SendTable '%s'.", pTable->GetName() ); |
|
|
|
bhs->m_pDatatableProps[bhs->m_nDatatableProps] = pProp; |
|
pChild->m_iDatatableProp = bhs->m_nDatatableProps; |
|
++bhs->m_nDatatableProps; |
|
|
|
pNode->m_Children.AddToTail( pChild ); |
|
|
|
// Recurse into the new child datatable. |
|
SendTable_BuildHierarchy( pChild, pProp->GetDataTable(), bhs ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( nNonDatatableProps >= MAX_TOTAL_SENDTABLE_PROPS ) |
|
Error( "SendTable_BuildHierarchy: overflowed non-datatable props with '%s'.", pProp->GetName() ); |
|
|
|
pNonDatatableProps[nNonDatatableProps] = pProp; |
|
++nNonDatatableProps; |
|
} |
|
} |
|
} |
|
|
|
|
|
void SendTable_BuildHierarchy( |
|
CSendNode *pNode, |
|
const SendTable *pTable, |
|
CBuildHierarchyStruct *bhs |
|
) |
|
{ |
|
pNode->m_pTable = pTable; |
|
pNode->m_iFirstRecursiveProp = bhs->m_nProps; |
|
|
|
Assert( bhs->m_nPropProxies < 255 ); |
|
unsigned char curPropProxy = bhs->m_nPropProxies; |
|
++bhs->m_nPropProxies; |
|
|
|
const SendProp *pNonDatatableProps[MAX_TOTAL_SENDTABLE_PROPS]; |
|
int nNonDatatableProps = 0; |
|
|
|
// First add all the child datatables. |
|
SendTable_BuildHierarchy_IterateProps( |
|
pNode, |
|
pTable, |
|
bhs, |
|
pNonDatatableProps, |
|
nNonDatatableProps ); |
|
|
|
|
|
// Now add the properties. |
|
|
|
// Make sure there's room, then just copy the pointers from the loop above. |
|
ErrorIfNot( bhs->m_nProps + nNonDatatableProps < ARRAYSIZE( bhs->m_pProps ), |
|
("SendTable_BuildHierarchy: overflowed prop buffer.") |
|
); |
|
|
|
for ( int i=0; i < nNonDatatableProps; i++ ) |
|
{ |
|
bhs->m_pProps[bhs->m_nProps] = pNonDatatableProps[i]; |
|
bhs->m_PropProxyIndices[bhs->m_nProps] = curPropProxy; |
|
++bhs->m_nProps; |
|
} |
|
|
|
pNode->m_nRecursiveProps = bhs->m_nProps - pNode->m_iFirstRecursiveProp; |
|
} |
|
|
|
void SendTable_SortByPriority(CBuildHierarchyStruct *bhs) |
|
{ |
|
int i, start = 0; |
|
|
|
while( true ) |
|
{ |
|
for ( i = start; i < bhs->m_nProps; i++ ) |
|
{ |
|
const SendProp *p = bhs->m_pProps[i]; |
|
unsigned char c = bhs->m_PropProxyIndices[i]; |
|
|
|
if ( p->GetFlags() & SPROP_CHANGES_OFTEN ) |
|
{ |
|
bhs->m_pProps[i] = bhs->m_pProps[start]; |
|
bhs->m_PropProxyIndices[i] = bhs->m_PropProxyIndices[start]; |
|
bhs->m_pProps[start] = p; |
|
bhs->m_PropProxyIndices[start] = c; |
|
start++; |
|
break; |
|
} |
|
} |
|
|
|
if ( i == bhs->m_nProps ) |
|
return; |
|
} |
|
} |
|
|
|
|
|
void CalcPathLengths_R( CSendNode *pNode, CUtlVector<int> &pathLengths, int curPathLength, int &totalPathLengths ) |
|
{ |
|
pathLengths[pNode->GetRecursiveProxyIndex()] = curPathLength; |
|
totalPathLengths += curPathLength; |
|
|
|
for ( int i=0; i < pNode->GetNumChildren(); i++ ) |
|
{ |
|
CalcPathLengths_R( pNode->GetChild( i ), pathLengths, curPathLength+1, totalPathLengths ); |
|
} |
|
} |
|
|
|
|
|
void FillPathEntries_R( CSendTablePrecalc *pPrecalc, CSendNode *pNode, CSendNode *pParent, int &iCurEntry ) |
|
{ |
|
// Fill in this node's path. |
|
CSendTablePrecalc::CProxyPath &outProxyPath = pPrecalc->m_ProxyPaths[ pNode->GetRecursiveProxyIndex() ]; |
|
outProxyPath.m_iFirstEntry = (unsigned short)iCurEntry; |
|
|
|
// Copy all the proxies leading to the parent. |
|
if ( pParent ) |
|
{ |
|
CSendTablePrecalc::CProxyPath &parentProxyPath = pPrecalc->m_ProxyPaths[pParent->GetRecursiveProxyIndex()]; |
|
outProxyPath.m_nEntries = parentProxyPath.m_nEntries + 1; |
|
|
|
for ( int i=0; i < parentProxyPath.m_nEntries; i++ ) |
|
pPrecalc->m_ProxyPathEntries[iCurEntry++] = pPrecalc->m_ProxyPathEntries[parentProxyPath.m_iFirstEntry+i]; |
|
|
|
// Now add this node's own proxy. |
|
pPrecalc->m_ProxyPathEntries[iCurEntry].m_iProxy = pNode->GetRecursiveProxyIndex(); |
|
pPrecalc->m_ProxyPathEntries[iCurEntry].m_iDatatableProp = pNode->m_iDatatableProp; |
|
++iCurEntry; |
|
} |
|
else |
|
{ |
|
outProxyPath.m_nEntries = 0; |
|
} |
|
|
|
for ( int i=0; i < pNode->GetNumChildren(); i++ ) |
|
{ |
|
FillPathEntries_R( pPrecalc, pNode->GetChild( i ), pNode, iCurEntry ); |
|
} |
|
} |
|
|
|
|
|
void SendTable_GenerateProxyPaths( CSendTablePrecalc *pPrecalc, int nProxyIndices ) |
|
{ |
|
// Initialize the array. |
|
pPrecalc->m_ProxyPaths.SetSize( nProxyIndices ); |
|
for ( int i=0; i < nProxyIndices; i++ ) |
|
pPrecalc->m_ProxyPaths[i].m_iFirstEntry = pPrecalc->m_ProxyPaths[i].m_nEntries = 0xFFFF; |
|
|
|
// Figure out how long the path down the tree is to each node. |
|
int totalPathLengths = 0; |
|
CUtlVector<int> pathLengths; |
|
pathLengths.SetSize( nProxyIndices ); |
|
memset( pathLengths.Base(), 0, sizeof( pathLengths[0] ) * nProxyIndices ); |
|
CalcPathLengths_R( pPrecalc->GetRootNode(), pathLengths, 0, totalPathLengths ); |
|
|
|
// |
|
int iCurEntry = 0; |
|
pPrecalc->m_ProxyPathEntries.SetSize( totalPathLengths ); |
|
FillPathEntries_R( pPrecalc, pPrecalc->GetRootNode(), NULL, iCurEntry ); |
|
} |
|
|
|
|
|
bool CSendTablePrecalc::SetupFlatPropertyArray() |
|
{ |
|
SendTable *pTable = GetSendTable(); |
|
|
|
// First go through and set SPROP_INSIDEARRAY when appropriate, and set array prop pointers. |
|
SetupArrayProps_R<SendTable, SendTable::PropType>( pTable ); |
|
|
|
// Make a list of which properties are excluded. |
|
ExcludeProp excludeProps[MAX_EXCLUDE_PROPS]; |
|
int nExcludeProps = 0; |
|
if( !SendTable_GetPropsExcluded( pTable, excludeProps, nExcludeProps, MAX_EXCLUDE_PROPS ) ) |
|
return false; |
|
|
|
// Now build the hierarchy. |
|
CBuildHierarchyStruct bhs; |
|
bhs.m_pExcludeProps = excludeProps; |
|
bhs.m_nExcludeProps = nExcludeProps; |
|
bhs.m_nProps = bhs.m_nDatatableProps = 0; |
|
bhs.m_nPropProxies = 0; |
|
SendTable_BuildHierarchy( GetRootNode(), pTable, &bhs ); |
|
|
|
SendTable_SortByPriority( &bhs ); |
|
|
|
// Copy the SendProp pointers into the precalc. |
|
MEM_ALLOC_CREDIT(); |
|
m_Props.CopyArray( bhs.m_pProps, bhs.m_nProps ); |
|
m_DatatableProps.CopyArray( bhs.m_pDatatableProps, bhs.m_nDatatableProps ); |
|
m_PropProxyIndices.CopyArray( bhs.m_PropProxyIndices, bhs.m_nProps ); |
|
|
|
// Assign the datatable proxy indices. |
|
SetNumDataTableProxies( 0 ); |
|
SetDataTableProxyIndices_R( this, GetRootNode(), &bhs ); |
|
|
|
int nProxyIndices = 0; |
|
SetRecursiveProxyIndices_R( pTable, GetRootNode(), nProxyIndices ); |
|
|
|
SendTable_GenerateProxyPaths( this, nProxyIndices ); |
|
return true; |
|
} |
|
|
|
|
|
// ---------------------------------------------------------------------------------------- // |
|
// Helpers. |
|
// ---------------------------------------------------------------------------------------- // |
|
|
|
// Compares two arrays of bits. |
|
// Returns true if they are equal. |
|
bool AreBitArraysEqual( |
|
void const *pvBits1, |
|
void const *pvBits2, |
|
int nBits ) |
|
{ |
|
unsigned int const *pBits1 = (unsigned int const *)pvBits1; |
|
unsigned int const *pBits2 = (unsigned int const *)pvBits2; |
|
|
|
// Compare words. |
|
int nWords = nBits >> 5; |
|
for ( int i = 0 ; i < nWords; ++i ) |
|
{ |
|
if ( pBits1[i] != pBits2[i] ) |
|
return false; |
|
} |
|
|
|
if ( nBits & 31 ) |
|
{ |
|
// Compare remaining bits. |
|
unsigned int mask = (1 << (nBits & 31)) - 1; |
|
return ((pBits1[nWords] ^ pBits2[nWords]) & mask) == 0; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
// Does a fast memcmp-based test to determine if the two bit arrays are different. |
|
// Returns true if they are equal. |
|
bool CompareBitArrays( |
|
void const *pPacked1, |
|
void const *pPacked2, |
|
int nBits1, |
|
int nBits2 |
|
) |
|
{ |
|
if( nBits1 >= 0 && nBits1 == nBits2 ) |
|
{ |
|
if ( pPacked1 == pPacked2 ) |
|
{ |
|
return true; |
|
} |
|
else |
|
{ |
|
return AreBitArraysEqual( pPacked1, pPacked2, nBits1 ); |
|
} |
|
} |
|
else |
|
return false; |
|
} |
|
|
|
// Looks at the DTWatchEnt and DTWatchProp console variables and returns true |
|
// if the user wants to watch this property. |
|
bool ShouldWatchThisProp( const SendTable *pTable, int objectID, const char *pPropName ) |
|
{ |
|
if(g_CV_DTWatchEnt.GetInt() != -1 && |
|
g_CV_DTWatchEnt.GetInt() == objectID) |
|
{ |
|
const char *pStr = g_CV_DTWatchVar.GetString(); |
|
if ( pStr && pStr[0] != 0 ) |
|
{ |
|
return stricmp( pStr, pPropName ) == 0; |
|
} |
|
else |
|
{ |
|
return true; |
|
} |
|
} |
|
|
|
if ( g_CV_DTWatchClass.GetString()[ 0 ] && Q_stristr( pTable->GetName(), g_CV_DTWatchClass.GetString() ) ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
bool Sendprop_UsingDebugWatch() |
|
{ |
|
if ( g_CV_DTWatchEnt.GetInt() != -1 ) |
|
return true; |
|
|
|
if ( g_CV_DTWatchClass.GetString()[ 0 ] ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|
|
// Prints a datatable warning into the console. |
|
void DataTable_Warning( const char *pInMessage, ... ) |
|
{ |
|
char msg[4096]; |
|
va_list marker; |
|
|
|
#if 0 |
|
#if !defined(_DEBUG) |
|
if(!g_CV_DTWarning.GetInt()) |
|
return; |
|
#endif |
|
#endif |
|
|
|
va_start(marker, pInMessage); |
|
Q_vsnprintf( msg, sizeof( msg ), pInMessage, marker); |
|
va_end(marker); |
|
|
|
Warning( "DataTable warning: %s", msg ); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|