//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef PREDICTIONCOPY_H #define PREDICTIONCOPY_H #ifdef _WIN32 #pragma once #endif #include #include "datamap.h" #include "ehandle.h" #include "tier1/utlstring.h" #include "tier1/utlrbtree.h" #include "tier1/utlstack.h" #if defined( CLIENT_DLL ) class C_BaseEntity; typedef CHandle EHANDLE; // #define COPY_CHECK_STRESSTEST #if defined( COPY_CHECK_STRESSTEST ) class IGameSystem; IGameSystem* GetPredictionCopyTester( void ); #endif #else class CBaseEntity; typedef CHandle EHANDLE; #endif typedef void ( *FN_FIELD_COMPARE )( const char *classname, const char *fieldname, const char *fieldtype, bool networked, bool noterrorchecked, bool differs, bool withintolerance, const char *value ); // Each datamap_t is broken down into two flattened arrays of fields, // one for PC_NETWORKED_DATA and one for PC_NON_NETWORKED_ONLY (optimized_datamap_t::datamapinfo_t::flattenedoffsets_t) // Each flattened array is sorted by offset for better cache performance // Finally, contiguous "runs" off offsets are precomputed (optimized_datamap_t::datamapinfo_t::datacopyruns_t) for fast copy operations // A data run is a set of DEFINE_PRED_FIELD fields in a c++ object which are contiguous and can be processing // using a single memcpy operation struct datarun_t { datarun_t() : m_nStartFlatField( 0 ), m_nEndFlatField( 0 ), m_nLength( 0 ) { for ( int i = 0 ; i < TD_OFFSET_COUNT; ++i ) { m_nStartOffset[ i ] = 0; #ifdef _X360 // These are the offsets of the next run, for priming the L1 cache m_nPrefetchOffset[ i ] = 0; #endif } } // Indices of start/end fields in the flattened typedescription_t list int m_nStartFlatField; int m_nEndFlatField; // Offsets for run in the packed/unpacked data (I think the run starts need to be properly aligned) int m_nStartOffset[ TD_OFFSET_COUNT ]; #ifdef _X360 // These are the offsets of the next run, for priming the L1 cache int m_nPrefetchOffset[ TD_OFFSET_COUNT ]; #endif int m_nLength; }; struct datacopyruns_t { public: CUtlVector< datarun_t > m_vecRuns; }; struct flattenedoffsets_t { CUtlVector< typedescription_t > m_Flattened; int m_nPackedSize; // Contiguous memory to pack all of these together for TD_OFFSET_PACKED int m_nPackedStartOffset; }; struct datamapinfo_t { // Flattened list, with FIELD_EMBEDDED, FTYPEDESC_PRIVATE, // and FTYPEDESC_OVERRIDE (overridden) fields removed flattenedoffsets_t m_Flat; datacopyruns_t m_CopyRuns; }; struct optimized_datamap_t { // Optimized info for PC_NON_NETWORKED and PC_NETWORKED data datamapinfo_t m_Info[ PC_COPYTYPE_COUNT ]; }; class CPredictionCopy { public: typedef enum { DIFFERS = 0, IDENTICAL, WITHINTOLERANCE, } difftype_t; typedef enum { TRANSFERDATA_COPYONLY = 0, // Data copying only (uses runs) TRANSFERDATA_ERRORCHECK_NOSPEW, // Checks for errors, returns after first error found TRANSFERDATA_ERRORCHECK_SPEW, // checks for errors, reports all errors to console TRANSFERDATA_ERRORCHECK_DESCRIBE, // used by hud_pdump, dumps values, etc, for all fields } optype_t; CPredictionCopy( int type, byte *dest, bool dest_packed, const byte *src, bool src_packed, optype_t opType, FN_FIELD_COMPARE func = NULL ); int TransferData( const char *operation, int entindex, datamap_t *dmap ); static bool PrepareDataMap( datamap_t *dmap ); static const typedescription_t *FindFlatFieldByName( const char *fieldname, const datamap_t *dmap ); private: // Operations: void TransferDataCopyOnly( const datamap_t *dmap ); void TransferDataErrorCheckNoSpew( char const *pchOperation, const datamap_t *dmap ); void TransferDataErrorCheckSpew( char const *pchOperation, const datamap_t *dmap ); void TransferDataDescribe( char const *pchOperation, const datamap_t *dmap ); // Report function void ReportFieldsDiffer( const datamap_t *pCurrentMap, const typedescription_t *pField, const char *fmt, ... ); void OutputFieldDescription( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t dt, const char *fmt, ... ); // Helper for TransferDataCopyOnly void CopyFlatFieldsUsingRuns( const datamap_t *pCurrentMap, int nPredictionCopyType ); void CopyFlatFields( const datamap_t *pCurrentMap, int nPredictionCopyType ); template< class T > FORCEINLINE void CopyField( difftype_t difftype, T *outvalue, const T *invalue, int count ); // Helper for TransferDataErrorCheckNoSpew void ErrorCheckFlatFields_NoSpew( const datamap_t *pCurrentMap, int nPredictionCopyType ); template< class T > FORCEINLINE void ProcessField_Compare_NoSpew( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ); // Helper for TransferDataErrorCheckSpew void ErrorCheckFlatFields_Spew( const datamap_t *pCurrentMap, int nPredictionCopyType ); template< class T > FORCEINLINE void ProcessField_Compare_Spew( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ); // Helper for TransferDataDescribe void DescribeFields( const CUtlVector< const datamap_t * > &vecGroups, const datamap_t *pCurrentMap, int nPredictionCopyType ); // Main entry point template< class T > FORCEINLINE void ProcessField_Describe( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ); // Helpers for entity field watcher void DetermineWatchField( const char *operation, int entindex, const datamap_t *dmap ); void WatchMsg( const typedescription_t *pField, const char *fmt, ... ); void DumpWatchField( const typedescription_t *pField, const byte *outvalue, int count ); template< class T > FORCEINLINE void WatchField( const typedescription_t *pField, const T *outvalue, int count ); // Helper for ErrorCheck ops template< class T > FORCEINLINE difftype_t CompareField( const typedescription_t *pField, const T *outvalue, const T *invalue, int count ); // Used by TRANSFERDATA_ERRORCHECK_SPEW and by TRANSFERDATA_ERRORCHECK_DESCRIBE template< class T > FORCEINLINE void DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const T *outvalue, const T *invalue, int count ); private: optype_t m_OpType; int m_nType; byte *m_pDest; const byte *m_pSrc; int m_nDestOffsetIndex; int m_nSrcOffsetIndex; int m_nErrorCount; int m_nEntIndex; FN_FIELD_COMPARE m_FieldCompareFunc; const typedescription_t *m_pWatchField; char const *m_pOperation; CUtlStack< const typedescription_t * > m_FieldStack; }; typedef void (*FN_FIELD_DESCRIPTION)( const char *classname, const char *fieldname, const char *fieldtype, bool networked, const char *value ); // // Compare methods // // Specializations template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const float *outvalue, const float *invalue, int count ); template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const Vector *outvalue, const Vector *invalue, int count ); template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const Quaternion *outvalue, const Quaternion *invalue, int count ); template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const char *outvalue, const char *invalue, int count ); template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const EHANDLE *outvalue, const EHANDLE *invalue, int count ); template<> FORCEINLINE CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const color32 *outvalue, const color32 *invalue, int count ); // // Describe Methods // // Specializations template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const short *outvalue, const short *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const int *outvalue, const int *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const bool *outvalue, const bool *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const float *outvalue, const float *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const char *outvalue, const char *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const Vector* outValue, const Vector *inValue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const Quaternion* outValue, const Quaternion *inValue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const EHANDLE *outvalue, const EHANDLE *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const color32 *outvalue, const color32 *invalue, int count ); template<> FORCEINLINE void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const uint8 *outvalue, const uint8 *invalue, int count ); // // Watch Methods // // Specializations template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const short *outvalue,int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const int *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const bool *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const float *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const Vector *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const Quaternion *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const EHANDLE *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const char *outvalue, int count ); template<> FORCEINLINE void CPredictionCopy::WatchField( const typedescription_t *pField, const color32 *outvalue, int count ); // // Copy Methods // // specializations template<> FORCEINLINE void CPredictionCopy::CopyField( difftype_t difftype, char *outvalue, const char *invalue, int count ); template< class T > FORCEINLINE void CPredictionCopy::ProcessField_Compare_NoSpew( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ) { difftype_t difftype = CompareField( pField, pOutputData, pInputData, fieldSize ); if ( difftype == DIFFERS ) { ++m_nErrorCount; } } template< class T > FORCEINLINE void CPredictionCopy::ProcessField_Compare_Spew( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ) { difftype_t difftype = CompareField( pField, pOutputData, pInputData, fieldSize ); DescribeField( pCurrentMap, pField, difftype, pOutputData, pInputData, fieldSize ); } template< class T > FORCEINLINE void CPredictionCopy::ProcessField_Describe( const datamap_t *pCurrentMap, const typedescription_t *pField, const T *pOutputData, const T *pInputData, int fieldSize ) { difftype_t difftype = CompareField( pField, pOutputData, pInputData, fieldSize ); DescribeField( pCurrentMap, pField, difftype, pOutputData, pInputData, fieldSize ); } #if defined( CLIENT_DLL ) class CValueChangeTracker { public: CValueChangeTracker(); void Reset(); void StartTrack( char const *pchContext ); void EndTrack(); bool IsActive() const; void SetupTracking( C_BaseEntity *ent, char const *pchFieldName ); void ClearTracking(); void Spew(); C_BaseEntity *GetEntity(); private: enum { eChangeTrackerBufSize = 128, }; // Returns field size void GetValue( char *buf, size_t bufsize ); bool m_bActive : 1; bool m_bTracking : 1; EHANDLE m_hEntityToTrack; const typedescription_t *m_pTrackField; CUtlString m_strFieldName; CUtlString m_strContext; // First 128 bytes of data is all we will consider char m_OrigValueBuf[ eChangeTrackerBufSize ]; CUtlVector< CUtlString > m_History; }; extern CValueChangeTracker *g_pChangeTracker; class CValueChangeTrackerScope { public: CValueChangeTrackerScope( char const *pchContext ) { m_bCallEndTrack = true; g_pChangeTracker->StartTrack( pchContext ); } // Only calls Start/End if passed in entity matches entity to track CValueChangeTrackerScope( C_BaseEntity *pEntity, char const *pchContext ) { m_bCallEndTrack = g_pChangeTracker->GetEntity() == pEntity; if ( m_bCallEndTrack ) { g_pChangeTracker->StartTrack( pchContext ); } } ~CValueChangeTrackerScope() { if ( m_bCallEndTrack ) { g_pChangeTracker->EndTrack(); } } private: bool m_bCallEndTrack; }; #if defined( _DEBUG ) #define PREDICTION_TRACKVALUECHANGESCOPE( context ) CValueChangeTrackerScope scope( context ); #define PREDICTION_TRACKVALUECHANGESCOPE_ENTITY( entity, context ) CValueChangeTrackerScope scope( entity, context ); #define PREDICTION_STARTTRACKVALUE( context ) g_pChangeTracker->StartTrack( context ); #define PREDICTION_ENDTRACKVALUE() g_pChangeTracker->EndTrack(); #define PREDICTION_SPEWVALUECHANGES() g_pChangeTracker->Spew(); #else #define PREDICTION_TRACKVALUECHANGESCOPE( context ) #define PREDICTION_TRACKVALUECHANGESCOPE_ENTITY( entity, context ) #define PREDICTION_STARTTRACKVALUE( context ) #define PREDICTION_ENDTRACKVALUE() #define PREDICTION_SPEWVALUECHANGES() #endif #endif // !CLIENT_DLL #endif // PREDICTIONCOPY_H