summaryrefslogtreecommitdiff
path: root/engine/dt_recv_eng.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/dt_recv_eng.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/dt_recv_eng.cpp')
-rw-r--r--engine/dt_recv_eng.cpp669
1 files changed, 669 insertions, 0 deletions
diff --git a/engine/dt_recv_eng.cpp b/engine/dt_recv_eng.cpp
new file mode 100644
index 0000000..b9127e5
--- /dev/null
+++ b/engine/dt_recv_eng.cpp
@@ -0,0 +1,669 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include "dt.h"
+#include "dt_recv_eng.h"
+#include "dt_encode.h"
+#include "dt_instrumentation.h"
+#include "dt_stack.h"
+#include "utllinkedlist.h"
+#include "tier0/dbg.h"
+#include "dt_recv_decoder.h"
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+#include "dt_common_eng.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+class CClientSendTable;
+
+
+// Testing out this pattern.. you can write simple code blocks inside of
+// codeToRun. The thing that sucks is that you can't access your function's
+// local variables inside of codeToRun.
+//
+// If it used an iterator class, it could access local function variables,
+// but the iterator class might be more trouble than it's worth.
+#define FOR_EACH_PROP_R( TableType, pTablePointer, tableCode, propCode ) \
+ class CPropVisitor \
+ { \
+ public: \
+ static void Visit_R( TableType *pTable ) \
+ { \
+ tableCode; \
+ \
+ for ( int i=0; i < pTable->GetNumProps(); i++ ) \
+ { \
+ TableType::PropType *pProp = pTable->GetProp( i ); \
+ \
+ propCode; \
+ \
+ if ( pProp->GetType() == DPT_DataTable ) \
+ Visit_R( pProp->GetDataTable() ); \
+ } \
+ } \
+ }; \
+ CPropVisitor::Visit_R( pTablePointer );
+
+#define SENDPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( SendTable, pTablePointer, tableCode, propCode )
+#define RECVPROP_VISIT( pTablePointer, tableCode, propCode ) FOR_EACH_PROP_R( RecvTable, pTablePointer, tableCode, propCode )
+#define SETUP_VISIT() class CDummyClass {} // Workaround for parser bug in VC7.1
+
+// ------------------------------------------------------------------------------------ //
+// Globals.
+// ------------------------------------------------------------------------------------ //
+
+CUtlLinkedList< RecvTable*, unsigned short > g_RecvTables;
+CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders;
+CUtlLinkedList< CClientSendTable*, unsigned short > g_ClientSendTables;
+
+int g_nPropsDecoded = 0;
+
+
+// ------------------------------------------------------------------------------------ //
+// Static helper functions.
+// ------------------------------------------------------------------------------------ //
+
+RecvTable* FindRecvTable( const char *pName )
+{
+ FOR_EACH_LL( g_RecvTables, i )
+ {
+ if ( stricmp( g_RecvTables[i]->GetName(), pName ) == 0 )
+ return g_RecvTables[i];
+ }
+ return 0;
+}
+
+
+static CClientSendTable* FindClientSendTable( const char *pName )
+{
+ FOR_EACH_LL( g_ClientSendTables, i )
+ {
+ CClientSendTable *pTable = g_ClientSendTables[i];
+
+ if ( stricmp( pTable->GetName(), pName ) == 0 )
+ return pTable;
+ }
+
+ return NULL;
+}
+
+
+// Find all child datatable properties for the send tables.
+bool SetupClientSendTableHierarchy()
+{
+ FOR_EACH_LL( g_ClientSendTables, iClientTable )
+ {
+ CClientSendTable *pTable = g_ClientSendTables[iClientTable];
+
+ // For each datatable property, find the table it references.
+ for ( int iProp=0; iProp < pTable->GetNumProps(); iProp++ )
+ {
+ CClientSendProp *pClientProp = pTable->GetClientProp( iProp );
+ SendProp *pProp = &pTable->m_SendTable.m_pProps[iProp];
+
+ if ( pProp->m_Type == DPT_DataTable )
+ {
+ const char *pTableName = pClientProp->GetTableName();
+ ErrorIfNot( pTableName,
+ ("SetupClientSendTableHierarchy: missing table name for prop '%s'.", pProp->GetName())
+ );
+
+ CClientSendTable *pChild = FindClientSendTable( pTableName );
+ if ( !pChild )
+ {
+ DataTable_Warning( "SetupClientSendTableHierarchy: missing SendTable '%s' (referenced by '%s').\n", pTableName, pTable->GetName() );
+ return false;
+ }
+
+ pProp->SetDataTable( &pChild->m_SendTable );
+ }
+ }
+ }
+
+ return true;
+}
+
+
+static RecvProp* FindRecvProp( RecvTable *pTable, const char *pName )
+{
+ for ( int i=0; i < pTable->GetNumProps(); i++ )
+ {
+ RecvProp *pProp = pTable->GetProp( i );
+
+ if ( stricmp( pProp->GetName(), pName ) == 0 )
+ return pProp;
+ }
+
+ return NULL;
+}
+
+
+// See if the RecvProp is fit to receive the SendProp's data.
+bool CompareRecvPropToSendProp( const RecvProp *pRecvProp, const SendProp *pSendProp )
+{
+ while ( 1 )
+ {
+ ErrorIfNot( pRecvProp && pSendProp,
+ ("CompareRecvPropToSendProp: missing a property.")
+ );
+
+ if ( pRecvProp->GetType() != pSendProp->GetType() || pRecvProp->IsInsideArray() != pSendProp->IsInsideArray() )
+ {
+ return false;
+ }
+
+ if ( pRecvProp->GetType() == DPT_Array )
+ {
+ if ( pRecvProp->GetNumElements() != pSendProp->GetNumElements() )
+ return false;
+
+ pRecvProp = pRecvProp->GetArrayProp();
+ pSendProp = pSendProp->GetArrayProp();
+ }
+ else
+ {
+ return true;
+ }
+ }
+}
+
+struct MatchingProp_t
+{
+ SendProp *m_pProp;
+ RecvProp *m_pMatchingRecvProp;
+
+ static bool LessFunc( const MatchingProp_t& lhs, const MatchingProp_t& rhs )
+ {
+ return lhs.m_pProp < rhs.m_pProp;
+ }
+};
+
+static bool MatchRecvPropsToSendProps_R( CUtlRBTree< MatchingProp_t, unsigned short >& lookup, char const *sendTableName, SendTable *pSendTable, RecvTable *pRecvTable, bool bAllowMismatches, bool *pAnyMismatches )
+{
+ for ( int i=0; i < pSendTable->m_nProps; i++ )
+ {
+ SendProp *pSendProp = &pSendTable->m_pProps[i];
+
+ if ( pSendProp->IsExcludeProp() || pSendProp->IsInsideArray() )
+ continue;
+
+ // Find a RecvProp by the same name and type.
+ RecvProp *pRecvProp = 0;
+ if ( pRecvTable )
+ pRecvProp = FindRecvProp( pRecvTable, pSendProp->GetName() );
+
+ if ( pRecvProp )
+ {
+ if ( !CompareRecvPropToSendProp( pRecvProp, pSendProp ) )
+ {
+ Warning( "RecvProp type doesn't match server type for %s/%s\n", pSendTable->GetName(), pSendProp->GetName() );
+ return false;
+ }
+
+ MatchingProp_t info;
+ info.m_pProp = pSendProp;
+ info.m_pMatchingRecvProp = pRecvProp;
+
+ lookup.Insert( info );
+ }
+ else
+ {
+ if ( pAnyMismatches )
+ {
+ *pAnyMismatches = true;
+ }
+
+ Warning( "Missing RecvProp for %s - %s/%s\n", sendTableName, pSendTable->GetName(), pSendProp->GetName() );
+ if ( !bAllowMismatches )
+ {
+ return false;
+ }
+ }
+
+ // Recurse.
+ if ( pSendProp->GetType() == DPT_DataTable )
+ {
+ if ( !MatchRecvPropsToSendProps_R( lookup, sendTableName, pSendProp->GetDataTable(), FindRecvTable( pSendProp->GetDataTable()->m_pNetTableName ), bAllowMismatches, pAnyMismatches ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+// ------------------------------------------------------------------------------------ //
+// Interface functions.
+// ------------------------------------------------------------------------------------ //
+bool RecvTable_Init( RecvTable **pTables, int nTables )
+{
+ SETUP_VISIT();
+
+ for ( int i=0; i < nTables; i++ )
+ {
+ RECVPROP_VISIT( pTables[i],
+ {
+ if ( pTable->IsInMainList() )
+ return;
+
+ // Shouldn't have a decoder yet.
+ ErrorIfNot( !pTable->m_pDecoder,
+ ("RecvTable_Init: table '%s' has a decoder already.", pTable->GetName()));
+
+ pTable->SetInMainList( true );
+ g_RecvTables.AddToTail( pTable );
+ },
+
+ {}
+ );
+ }
+
+ return true;
+}
+
+
+void RecvTable_Term( bool clearall /*= true*/ )
+{
+ DTI_Term();
+
+ SETUP_VISIT();
+
+ FOR_EACH_LL( g_RecvTables, i )
+ {
+ RECVPROP_VISIT( g_RecvTables[i],
+ {
+ if ( !pTable->IsInMainList() )
+ return;
+
+ pTable->SetInMainList( false );
+ pTable->m_pDecoder = 0;
+ },
+
+ {}
+ );
+ }
+
+ if ( clearall )
+ {
+ g_RecvTables.Purge();
+ }
+ g_RecvDecoders.PurgeAndDeleteElements();
+ g_ClientSendTables.PurgeAndDeleteElements();
+}
+
+void RecvTable_FreeSendTable( SendTable *pTable )
+{
+ for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
+ {
+ SendProp *pProp = &pTable->m_pProps[iProp];
+
+ delete [] pProp->m_pVarName;
+
+ if ( pProp->m_pExcludeDTName )
+ delete [] pProp->m_pExcludeDTName;
+ }
+
+ if ( pTable->m_pProps )
+ delete [] pTable->m_pProps;
+
+ delete pTable;
+}
+
+
+SendTable *RecvTable_ReadInfos( bf_read *pBuf, int nDemoProtocol )
+{
+ SendTable *pTable = new SendTable;
+
+ pTable->m_pNetTableName = pBuf->ReadAndAllocateString();
+
+ // Read the property list.
+ pTable->m_nProps = pBuf->ReadUBitLong( PROPINFOBITS_NUMPROPS );
+ pTable->m_pProps = pTable->m_nProps ? new SendProp[ pTable->m_nProps ] : NULL;
+
+ for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
+ {
+ SendProp *pProp = &pTable->m_pProps[iProp];
+
+ pProp->m_Type = (SendPropType)pBuf->ReadUBitLong( PROPINFOBITS_TYPE );
+ pProp->m_pVarName = pBuf->ReadAndAllocateString();
+
+ int nFlagsBits = PROPINFOBITS_FLAGS;
+
+ // HACK to playback old demos. SPROP_NUMFLAGBITS was 11, now 13
+ // old nDemoProtocol was 2
+ if ( nDemoProtocol == 2 )
+ {
+ nFlagsBits = 11;
+ }
+
+ pProp->SetFlags( pBuf->ReadUBitLong( nFlagsBits ) );
+
+ if ( pProp->m_Type == DPT_DataTable )
+ {
+ pProp->m_pExcludeDTName = pBuf->ReadAndAllocateString();
+ }
+ else
+ {
+ if ( pProp->IsExcludeProp() )
+ {
+ pProp->m_pExcludeDTName = pBuf->ReadAndAllocateString();
+ }
+ else if ( pProp->GetType() == DPT_Array )
+ {
+ pProp->SetNumElements( pBuf->ReadUBitLong( PROPINFOBITS_NUMELEMENTS ) );
+ }
+ else
+ {
+ pProp->m_fLowValue = pBuf->ReadBitFloat();
+ pProp->m_fHighValue = pBuf->ReadBitFloat();
+ pProp->m_nBits = pBuf->ReadUBitLong( PROPINFOBITS_NUMBITS );
+ }
+ }
+ }
+
+ return pTable;
+}
+
+bool RecvTable_RecvClassInfos( bf_read *pBuf, bool bNeedsDecoder, int nDemoProtocol )
+{
+ SendTable *pSendTable = RecvTable_ReadInfos( pBuf, nDemoProtocol );
+
+ if ( !pSendTable )
+ return false;
+
+ bool ret = DataTable_SetupReceiveTableFromSendTable( pSendTable, bNeedsDecoder );
+
+ RecvTable_FreeSendTable( pSendTable );
+
+ return ret;
+}
+
+static void CopySendPropsToRecvProps(
+ CUtlRBTree< MatchingProp_t, unsigned short >& lookup,
+ const CUtlVector<const SendProp*> &sendProps,
+ CUtlVector<const RecvProp*> &recvProps
+ )
+{
+ recvProps.SetSize( sendProps.Count() );
+ for ( int iSendProp=0; iSendProp < sendProps.Count(); iSendProp++ )
+ {
+ const SendProp *pSendProp = sendProps[iSendProp];
+ MatchingProp_t search;
+ search.m_pProp = (SendProp *)pSendProp;
+ int idx = lookup.Find( search );
+ if ( idx == lookup.InvalidIndex() )
+ {
+ recvProps[iSendProp] = 0;
+ }
+ else
+ {
+ recvProps[iSendProp] = lookup[ idx ].m_pMatchingRecvProp;
+ }
+ }
+}
+
+bool RecvTable_CreateDecoders( const CStandardSendProxies *pSendProxies, bool bAllowMismatches, bool *pAnyMismatches )
+{
+ DTI_Init();
+
+ SETUP_VISIT();
+
+ if ( pAnyMismatches )
+ {
+ *pAnyMismatches = false;
+ }
+
+ // First, now that we've supposedly received all the SendTables that we need,
+ // set their datatable child pointers.
+ if ( !SetupClientSendTableHierarchy() )
+ return false;
+
+ FOR_EACH_LL( g_RecvDecoders, i )
+ {
+ CRecvDecoder *pDecoder = g_RecvDecoders[i];
+
+
+ // It should already have been linked to its ClientSendTable.
+ Assert( pDecoder->m_pClientSendTable );
+ if ( !pDecoder->m_pClientSendTable )
+ return false;
+
+
+ // For each decoder, precalculate the SendTable's flat property list.
+ if ( !pDecoder->m_Precalc.SetupFlatPropertyArray() )
+ return false;
+
+ CUtlRBTree< MatchingProp_t, unsigned short > PropLookup( 0, 0, MatchingProp_t::LessFunc );
+
+ // Now match RecvProp with SendProps.
+ if ( !MatchRecvPropsToSendProps_R( PropLookup, pDecoder->GetSendTable()->m_pNetTableName, pDecoder->GetSendTable(), FindRecvTable( pDecoder->GetSendTable()->m_pNetTableName ), bAllowMismatches, pAnyMismatches ) )
+ return false;
+
+ // Now fill out the matching RecvProp array.
+ CSendTablePrecalc *pPrecalc = &pDecoder->m_Precalc;
+ CopySendPropsToRecvProps( PropLookup, pPrecalc->m_Props, pDecoder->m_Props );
+ CopySendPropsToRecvProps( PropLookup, pPrecalc->m_DatatableProps, pDecoder->m_DatatableProps );
+
+ DTI_HookRecvDecoder( pDecoder );
+ }
+
+ return true;
+}
+
+
+bool RecvTable_Decode(
+ RecvTable *pTable,
+ void *pStruct,
+ bf_read *pIn,
+ int objectID,
+ bool updateDTI
+ )
+{
+ CRecvDecoder *pDecoder = pTable->m_pDecoder;
+ ErrorIfNot( pDecoder,
+ ("RecvTable_Decode: table '%s' missing a decoder.", pTable->GetName())
+ );
+
+ // While there are properties, decode them.. walk the stack as you go.
+ CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID );
+
+ theStack.Init();
+ int iStartBit = 0, nIndexBits = 0, iLastBit = pIn->GetNumBitsRead();
+ unsigned int iProp;
+ CDeltaBitsReader deltaBitsReader( pIn );
+ while ( (iProp = deltaBitsReader.ReadNextPropIndex()) < MAX_DATATABLE_PROPS )
+ {
+ theStack.SeekToProp( iProp );
+
+ const RecvProp *pProp = pDecoder->GetProp( iProp );
+
+ // Instrumentation (store the # bits for the prop index).
+ if ( g_bDTIEnabled )
+ {
+ iStartBit = pIn->GetNumBitsRead();
+ nIndexBits = iStartBit - iLastBit;
+ }
+
+ DecodeInfo decodeInfo;
+ decodeInfo.m_pStruct = theStack.GetCurStructBase();
+
+ if ( pProp )
+ {
+ decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset();
+ }
+ else
+ {
+ // They're allowed to be missing props here if they're playing back a demo.
+ // This allows us to change the datatables and still preserve old demos.
+ decodeInfo.m_pData = NULL;
+ }
+
+ decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed.
+ decodeInfo.m_pProp = pDecoder->GetSendProp( iProp );
+ decodeInfo.m_pIn = pIn;
+ decodeInfo.m_ObjectID = objectID;
+
+ g_PropTypeFns[ decodeInfo.m_pProp->GetType() ].Decode( &decodeInfo );
+ ++g_nPropsDecoded;
+
+ // Instrumentation (store # bits for the encoded property).
+ if ( updateDTI && g_bDTIEnabled )
+ {
+ iLastBit = pIn->GetNumBitsRead();
+ DTI_HookDeltaBits( pDecoder, iProp, iLastBit - iStartBit, nIndexBits );
+ }
+ }
+
+ return !pIn->IsOverflowed();
+}
+
+
+void RecvTable_DecodeZeros( RecvTable *pTable, void *pStruct, int objectID )
+{
+ CRecvDecoder *pDecoder = pTable->m_pDecoder;
+ ErrorIfNot( pDecoder,
+ ("RecvTable_DecodeZeros: table '%s' missing a decoder.", pTable->GetName())
+ );
+
+ // While there are properties, decode them.. walk the stack as you go.
+ CClientDatatableStack theStack( pDecoder, (unsigned char*)pStruct, objectID );
+
+ theStack.Init();
+
+ for ( int iProp=0; iProp < pDecoder->GetNumProps(); iProp++ )
+ {
+ theStack.SeekToProp( iProp );
+
+ // They're allowed to be missing props here if they're playing back a demo.
+ // This allows us to change the datatables and still preserve old demos.
+ const RecvProp *pProp = pDecoder->GetProp( iProp );
+ if ( !pProp )
+ continue;
+
+ DecodeInfo decodeInfo;
+ decodeInfo.m_pStruct = theStack.GetCurStructBase();
+ decodeInfo.m_pData = theStack.GetCurStructBase() + pProp->GetOffset();
+ decodeInfo.m_pRecvProp = theStack.IsCurProxyValid() ? pProp : NULL; // Just skip the data if the proxies are screwed.
+ decodeInfo.m_pProp = pDecoder->GetSendProp( iProp );
+ decodeInfo.m_pIn = NULL;
+ decodeInfo.m_ObjectID = objectID;
+
+ g_PropTypeFns[pProp->GetType()].DecodeZero( &decodeInfo );
+ }
+}
+
+
+
+int RecvTable_MergeDeltas(
+ RecvTable *pTable,
+
+ bf_read *pOldState, // this can be null
+ bf_read *pNewState,
+
+ bf_write *pOut,
+
+ int objectID,
+ int *pChangedProps,
+ bool updateDTI
+ )
+{
+ ErrorIfNot( pTable && pNewState && pOut,
+ ("RecvTable_MergeDeltas: invalid parameters passed.")
+ );
+
+ CRecvDecoder *pDecoder = pTable->m_pDecoder;
+ ErrorIfNot( pDecoder, ("RecvTable_MergeDeltas: table '%s' is missing its decoder.", pTable->GetName()) );
+
+ int nChanged = 0;
+
+ // Setup to read the delta bits from each buffer.
+ CDeltaBitsReader oldStateReader( pOldState );
+ CDeltaBitsReader newStateReader( pNewState );
+
+ // Setup to write delta bits into the output.
+ CDeltaBitsWriter deltaBitsWriter( pOut );
+
+ unsigned int iOldProp = ~0u;
+ if ( pOldState )
+ iOldProp = oldStateReader.ReadNextPropIndex();
+
+ int iStartBit = 0, nIndexBits = 0, iLastBit = pNewState->GetNumBitsRead();
+
+ unsigned int iNewProp = newStateReader.ReadNextPropIndex();
+
+ while ( 1 )
+ {
+ // Write any properties in the previous state that aren't in the new state.
+ while ( iOldProp < iNewProp )
+ {
+ deltaBitsWriter.WritePropIndex( iOldProp );
+ oldStateReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pDecoder->GetSendProp( iOldProp ) );
+ iOldProp = oldStateReader.ReadNextPropIndex();
+ }
+
+ // Check if we're at the end here so the while() statement above can seek the old buffer
+ // to its end too.
+ if ( iNewProp >= MAX_DATATABLE_PROPS )
+ break;
+
+ // If the old state has this property too, then just skip over its data.
+ if ( iOldProp == iNewProp )
+ {
+ oldStateReader.SkipPropData( pDecoder->GetSendProp( iOldProp ) );
+ iOldProp = oldStateReader.ReadNextPropIndex();
+ }
+
+ // Instrumentation (store the # bits for the prop index).
+ if ( updateDTI && g_bDTIEnabled )
+ {
+ iStartBit = pNewState->GetNumBitsRead();
+ nIndexBits = iStartBit - iLastBit;
+ }
+
+ // Now write the new state's value.
+ deltaBitsWriter.WritePropIndex( iNewProp );
+ newStateReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pDecoder->GetSendProp( iNewProp ) );
+
+ if ( pChangedProps )
+ {
+ pChangedProps[nChanged] = iNewProp;
+ }
+
+ nChanged++;
+
+ // Instrumentation (store # bits for the encoded property).
+ if ( updateDTI && g_bDTIEnabled )
+ {
+ iLastBit = pNewState->GetNumBitsRead();
+ DTI_HookDeltaBits( pDecoder, iNewProp, iLastBit - iStartBit, nIndexBits );
+ }
+
+ iNewProp = newStateReader.ReadNextPropIndex();
+ }
+
+ Assert( nChanged <= MAX_DATATABLE_PROPS );
+
+ ErrorIfNot(
+ !(pOldState && pOldState->IsOverflowed()) && !pNewState->IsOverflowed() && !pOut->IsOverflowed(),
+ ("RecvTable_MergeDeltas: overflowed in RecvTable '%s'.", pTable->GetName())
+ );
+
+ return nChanged;
+}
+
+
+void RecvTable_CopyEncoding( RecvTable *pTable, bf_read *pIn, bf_write *pOut, int objectID )
+{
+ RecvTable_MergeDeltas( pTable, NULL, pIn, pOut, objectID );
+}
+
+
+