//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "dt_stack.h" #include "client.h" #include "host.h" #include "utllinkedlist.h" #include "server.h" #include "server_class.h" #include "eiface.h" #include "demo.h" #include "sv_packedentities.h" #include "tier0/icommandline.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern CUtlLinkedList< CClientSendTable*, unsigned short > g_ClientSendTables; extern CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders; RecvTable* FindRecvTable( const char *pName ); bool DataTable_SetupReceiveTableFromSendTable( SendTable *sendTable, bool bNeedsDecoder ) { CClientSendTable *pClientSendTable = new CClientSendTable; SendTable *pTable = &pClientSendTable->m_SendTable; g_ClientSendTables.AddToTail( pClientSendTable ); // Read the name. pTable->m_pNetTableName = COM_StringCopy( sendTable->m_pNetTableName ); // Create a decoder for it if necessary. if ( bNeedsDecoder ) { // Make a decoder for it. CRecvDecoder *pDecoder = new CRecvDecoder; g_RecvDecoders.AddToTail( pDecoder ); RecvTable *pRecvTable = FindRecvTable( pTable->m_pNetTableName ); if ( !pRecvTable ) { DataTable_Warning( "No matching RecvTable for SendTable '%s'.\n", pTable->m_pNetTableName ); return false; } pRecvTable->m_pDecoder = pDecoder; pDecoder->m_pTable = pRecvTable; pDecoder->m_pClientSendTable = pClientSendTable; pDecoder->m_Precalc.m_pSendTable = pClientSendTable->GetSendTable(); pClientSendTable->GetSendTable()->m_pPrecalc = &pDecoder->m_Precalc; // Initialize array properties. SetupArrayProps_R( pRecvTable ); } // Read the property list. pTable->m_nProps = sendTable->m_nProps; pTable->m_pProps = pTable->m_nProps ? new SendProp[ pTable->m_nProps ] : 0; pClientSendTable->m_Props.SetSize( pTable->m_nProps ); for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) { CClientSendProp *pClientProp = &pClientSendTable->m_Props[iProp]; SendProp *pProp = &pTable->m_pProps[iProp]; const SendProp *pSendTableProp = &sendTable->m_pProps[ iProp ]; pProp->m_Type = (SendPropType)pSendTableProp->m_Type; pProp->m_pVarName = COM_StringCopy( pSendTableProp->GetName() ); pProp->SetFlags( pSendTableProp->GetFlags() ); pProp->SetPriority( pSendTableProp->GetPriority() ); if ( CommandLine()->FindParm("-dti" ) && pSendTableProp->GetParentArrayPropName() ) { pProp->m_pParentArrayPropName = COM_StringCopy( pSendTableProp->GetParentArrayPropName() ); } if ( pProp->m_Type == DPT_DataTable ) { char *pDTName = pSendTableProp->m_pExcludeDTName; // HACK if ( pSendTableProp->GetDataTable() ) pDTName = pSendTableProp->GetDataTable()->m_pNetTableName; Assert( pDTName && Q_strlen(pDTName) > 0 ); pClientProp->SetTableName( COM_StringCopy( pDTName ) ); // Normally we wouldn't care about this but we need to compare it against // proxies in the server DLL in SendTable_BuildHierarchy. pProp->SetDataTableProxyFn( pSendTableProp->GetDataTableProxyFn() ); pProp->SetOffset( pSendTableProp->GetOffset() ); } else { if ( pProp->IsExcludeProp() ) { pProp->m_pExcludeDTName = COM_StringCopy( pSendTableProp->GetExcludeDTName() ); } else if ( pProp->GetType() == DPT_Array ) { pProp->SetNumElements( pSendTableProp->GetNumElements() ); } else { pProp->m_fLowValue = pSendTableProp->m_fLowValue; pProp->m_fHighValue = pSendTableProp->m_fHighValue; pProp->m_nBits = pSendTableProp->m_nBits; } } } return true; } // If the table's ID is -1, writes its info into the buffer and increments curID. void DataTable_MaybeCreateReceiveTable( CUtlVector< SendTable * >& visited, SendTable *pTable, bool bNeedDecoder ) { // Already sent? if ( visited.Find( pTable ) != visited.InvalidIndex() ) return; visited.AddToTail( pTable ); DataTable_SetupReceiveTableFromSendTable( pTable, bNeedDecoder ); } void DataTable_MaybeCreateReceiveTable_R( CUtlVector< SendTable * >& visited, SendTable *pTable ) { DataTable_MaybeCreateReceiveTable( visited, pTable, false ); // Make sure we send child send tables.. for(int i=0; i < pTable->m_nProps; i++) { SendProp *pProp = &pTable->m_pProps[i]; if( pProp->m_Type == DPT_DataTable ) { DataTable_MaybeCreateReceiveTable_R( visited, pProp->GetDataTable() ); } } } void DataTable_CreateClientTablesFromServerTables() { if ( !serverGameDLL ) { Sys_Error( "DataTable_CreateClientTablesFromServerTables: No serverGameDLL loaded!" ); } ServerClass *pClasses = serverGameDLL->GetAllServerClasses(); ServerClass *pCur; CUtlVector< SendTable * > visited; // First, we send all the leaf classes. These are the ones that will need decoders // on the client. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeCreateReceiveTable( visited, pCur->m_pTable, true ); } // Now, we send their base classes. These don't need decoders on the client // because we will never send these SendTables by themselves. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeCreateReceiveTable_R( visited, pCur->m_pTable ); } } void DataTable_CreateClientClassInfosFromServerClasses( CBaseClientState *pState ) { if ( !serverGameDLL ) { Sys_Error( "DataTable_CreateClientClassInfosFromServerClasses: No serverGameDLL loaded!" ); } ServerClass *pClasses = serverGameDLL->GetAllServerClasses(); // Count the number of classes. int nClasses = 0; for ( ServerClass *pCount=pClasses; pCount; pCount=pCount->m_pNext ) { ++nClasses; } // Remove old if ( pState->m_pServerClasses ) { delete [] pState->m_pServerClasses; } Assert( nClasses > 0 ); pState->m_nServerClasses = nClasses; pState->m_pServerClasses = new C_ServerClassInfo[ pState->m_nServerClasses ]; if ( !pState->m_pServerClasses ) { Host_EndGame(true, "CL_ParseClassInfo: can't allocate %d C_ServerClassInfos.\n", pState->m_nServerClasses); return; } // Now fill in the entries int curID = 0; for ( ServerClass *pClass=pClasses; pClass; pClass=pClass->m_pNext ) { Assert( pClass->m_ClassID >= 0 && pClass->m_ClassID < nClasses ); pClass->m_ClassID = curID++; pState->m_pServerClasses[ pClass->m_ClassID ].m_ClassName = COM_StringCopy( pClass->m_pNetworkName ); pState->m_pServerClasses[ pClass->m_ClassID ].m_DatatableName = COM_StringCopy( pClass->m_pTable->GetName() ); } } // If the table's ID is -1, writes its info into the buffer and increments curID. static void DataTable_MaybeWriteSendTableBuffer( SendTable *pTable, bf_write *pBuf, bool bNeedDecoder ) { // Already sent? if ( pTable->GetWriteFlag() ) return; pTable->SetWriteFlag( true ); SendTable_WriteInfos( pTable, *pBuf, bNeedDecoder, false ); } // Calls DataTable_MaybeWriteSendTable recursively. void DataTable_MaybeWriteSendTableBuffer_R( SendTable *pTable, bf_write *pBuf ) { DataTable_MaybeWriteSendTableBuffer( pTable, pBuf, false ); // Make sure we send child send tables.. for(int i=0; i < pTable->m_nProps; i++) { SendProp *pProp = &pTable->m_pProps[i]; if( pProp->m_Type == DPT_DataTable ) { DataTable_MaybeWriteSendTableBuffer_R( pProp->GetDataTable(), pBuf ); } } } #ifdef _DEBUG // If the table's ID is -1, writes its info into the buffer and increments curID. void DataTable_MaybeDumpSendTable( SendTable *pTable, bool bNeedDecoder ) { // Already sent? if ( pTable->GetWriteFlag() ) return; pTable->SetWriteFlag( true ); Msg( "\t%s has %d props:\n", pTable->GetName(), pTable->GetNumProps() ); for ( int iProp=0; iProp < pTable->m_nProps; iProp++ ) { const SendProp *pProp = &pTable->m_pProps[iProp]; Msg( "\t\t%s\n", pProp->GetName() ); } } // Calls DataTable_MaybeWriteSendTable recursively. void DataTable_MaybeDumpSendTable_R( SendTable *pTable ) { DataTable_MaybeDumpSendTable( pTable, false ); // Make sure we send child send tables.. for(int i=0; i < pTable->m_nProps; i++) { SendProp *pProp = &pTable->m_pProps[i]; if( pProp->m_Type == DPT_DataTable ) { DataTable_MaybeDumpSendTable_R( pProp->GetDataTable() ); } } } #endif void DataTable_ClearWriteFlags_R( SendTable *pTable ) { pTable->SetWriteFlag( false ); for(int i=0; i < pTable->m_nProps; i++) { SendProp *pProp = &pTable->m_pProps[i]; if( pProp->m_Type == DPT_DataTable ) { DataTable_ClearWriteFlags_R( pProp->GetDataTable() ); } } } void DataTable_ClearWriteFlags( ServerClass *pClasses ) { for ( ServerClass *pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_ClearWriteFlags_R( pCur->m_pTable ); } } void DataTable_WriteSendTablesBuffer( ServerClass *pClasses, bf_write *pBuf ) { ServerClass *pCur; DataTable_ClearWriteFlags( pClasses ); // First, we send all the leaf classes. These are the ones that will need decoders // on the client. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeWriteSendTableBuffer( pCur->m_pTable, pBuf, true ); } // Now, we send their base classes. These don't need decoders on the client // because we will never send these SendTables by themselves. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeWriteSendTableBuffer_R( pCur->m_pTable, pBuf ); } // Signal no more send tables. SendTable_WriteInfos( NULL, *pBuf, false, true ); } #ifdef _DEBUG void DataTable_DumpSendTables( ServerClass *pClasses ) { ServerClass *pCur; DataTable_ClearWriteFlags( pClasses ); // First, we send all the leaf classes. These are the ones that will need decoders // on the client. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeDumpSendTable( pCur->m_pTable, true ); } // Now, we send their base classes. These don't need decoders on the client // because we will never send these SendTables by themselves. for ( pCur=pClasses; pCur; pCur=pCur->m_pNext ) { DataTable_MaybeDumpSendTable_R( pCur->m_pTable ); } } #endif void DataTable_WriteClassInfosBuffer(ServerClass *pClasses, bf_write *pBuf ) { int count = 0; ServerClass *pClass = pClasses; // first count total number of classes in list while ( pClass != NULL ) { pClass=pClass->m_pNext; count++; } // write number of classes pBuf->WriteShort( count ); pClass = pClasses; // go back to first class // write each class info while ( pClass != NULL ) { pBuf->WriteShort( pClass->m_ClassID ); pBuf->WriteString( pClass->m_pNetworkName ); pBuf->WriteString( pClass->m_pTable->GetName() ); pClass=pClass->m_pNext; } } #ifdef _DEBUG void DataTable_DumpClassInfos(ServerClass *pClasses ) { int count = 0; ServerClass *pClass = pClasses; // first count total number of classes in list while ( pClass != NULL ) { pClass=pClass->m_pNext; count++; } // write number of classes Msg( "%d ClassInfos\n", count ); pClass = pClasses; // go back to first class // write each class info while ( pClass != NULL ) { Msg( "\t%d: %s / %s\n", pClass->m_ClassID, pClass->m_pNetworkName, pClass->m_pTable->GetName() ); pClass=pClass->m_pNext; } } #endif bool DataTable_ParseClassInfosFromBuffer( CClientState *pState, bf_read *pBuf ) { if(pState->m_pServerClasses) { delete [] pState->m_pServerClasses; } pState->m_nServerClasses = pBuf->ReadShort(); Assert( pState->m_nServerClasses ); pState->m_pServerClasses = new C_ServerClassInfo[pState->m_nServerClasses]; if ( !pState->m_pServerClasses ) { Host_EndGame(true, "CL_ParseClassInfo: can't allocate %d C_ServerClassInfos.\n", pState->m_nServerClasses); return false; } for ( int i = 0; i < pState->m_nServerClasses; i++ ) { int classID = pBuf->ReadShort(); if( classID >= pState->m_nServerClasses ) { Host_EndGame(true, "DataTable_ParseClassInfosFromBuffer: invalid class index (%d).\n", classID); return false; } pState->m_pServerClasses[classID].m_ClassName = pBuf->ReadAndAllocateString(); pState->m_pServerClasses[classID].m_DatatableName = pBuf->ReadAndAllocateString(); } return true; } bool DataTable_LoadDataTablesFromBuffer( bf_read *pBuf, int nDemoProtocol ) { // Okay, read them out of the buffer since they weren't recorded into the main network stream during recording CSVCMsg_SendTable_t msg; // Create all of the send tables locally // was DataTable_ParseClientTablesFromBuffer() while ( 1 ) { int type = pBuf->ReadVarInt32(); if( !msg.ReadFromBuffer( *pBuf ) ) { Host_Error( "DataTable_ParseClientTablesFromBuffer ReadFromBuffer failed.\n" ); return false; } int msgType = msg.GetType(); if ( msgType != type ) { Host_Error( "DataTable_ParseClientTablesFromBuffer ReadFromBuffer failed.\n" ); return false; } if( msg.is_end() ) break; if ( !RecvTable_RecvClassInfos( msg, nDemoProtocol ) ) { Host_Error( "DataTable_ParseClientTablesFromBuffer failed.\n" ); return false; } } #ifndef DEDICATED // Now create all of the server classes locally, too return DataTable_ParseClassInfosFromBuffer( &GetBaseLocalClient(), pBuf ); #else return false; #endif }