|
|
//========= Copyright (c) Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#if !defined( NO_ENTITY_PREDICTION )
#include "igamesystem.h"
#ifndef _PS3
#ifdef WIN32
#include <typeinfo.h>
#else
#include <typeinfo>
#endif
#endif
#include "cdll_int.h"
#ifndef _PS3
#include <memory.h>
#endif
#include <stdarg.h>
#include "tier0/dbg.h"
#include "tier1/strtools.h"
#include "predictioncopy.h"
#include "engine/ivmodelinfo.h"
#include "tier1/fmtstr.h"
#include "utlvector.h"
#include "tier0/vprof.h"
#include "tier1/tokenset.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
CClassMemoryPool< optimized_datamap_t > g_OptimizedDataMapPool( 20, CUtlMemoryPool::GROW_SLOW );
// --------------------------------------------------------------
//
// CSave
//
// --------------------------------------------------------------
static const char *g_FieldTypes[ FIELD_TYPECOUNT ] = { "FIELD_VOID", // FIELD_VOID
"FIELD_FLOAT", // FIELD_FLOAT
"FIELD_STRING", // FIELD_STRING
"FIELD_VECTOR", // FIELD_VECTOR
"FIELD_QUATERNION", // FIELD_QUATERNION
"FIELD_INTEGER", // FIELD_INTEGER
"FIELD_BOOLEAN", // FIELD_BOOLEAN
"FIELD_SHORT", // FIELD_SHORT
"FIELD_CHARACTER", // FIELD_CHARACTER
"FIELD_COLOR32", // FIELD_COLOR32
"FIELD_EMBEDDED", // FIELD_EMBEDDED (handled specially)
"FIELD_CUSTOM", // FIELD_CUSTOM (handled specially)
"FIELD_CLASSPTR", // FIELD_CLASSPTR
"FIELD_EHANDLE", // FIELD_EHANDLE
"FIELD_EDICT", // FIELD_EDICT
"FIELD_POSITION_VECTOR",// FIELD_POSITION_VECTOR
"FIELD_TIME", // FIELD_TIME
"FIELD_TICK", // FIELD_TICK
"FIELD_MODELNAME", // FIELD_MODELNAME
"FIELD_SOUNDNAME", // FIELD_SOUNDNAME
"FIELD_INPUT", // FIELD_INPUT (uses custom type)
"FIELD_FUNCTION", // FIELD_FUNCTION
"FIELD_VMATRIX", "FIELD_VMATRIX_WORLDSPACE", "FIELD_MATRIX3X4_WORLDSPACE", "FIELD_INTERVAL" // FIELD_INTERVAL
"FIELD_MODELINDEX" // FIELD_MODELINDEX
};
static int g_FieldSizes[FIELD_TYPECOUNT] = { 0, // FIELD_VOID
sizeof(float), // FIELD_FLOAT
sizeof(int), // FIELD_STRING
sizeof(Vector), // FIELD_VECTOR
sizeof(Quaternion), // FIELD_QUATERNION
sizeof(int), // FIELD_INTEGER
sizeof(char), // FIELD_BOOLEAN
sizeof(short), // FIELD_SHORT
sizeof(char), // FIELD_CHARACTER
sizeof(color32), // FIELD_COLOR32
sizeof(int), // FIELD_EMBEDDED (handled specially)
sizeof(int), // FIELD_CUSTOM (handled specially)
//---------------------------------
sizeof(int), // FIELD_CLASSPTR
sizeof(EHANDLE), // FIELD_EHANDLE
sizeof(int), // FIELD_EDICT
sizeof(Vector), // FIELD_POSITION_VECTOR
sizeof(float), // FIELD_TIME
sizeof(int), // FIELD_TICK
sizeof(int), // FIELD_MODELNAME
sizeof(int), // FIELD_SOUNDNAME
sizeof(int), // FIELD_INPUT (uses custom type)
sizeof(int *), // FIELD_FUNCTION
sizeof(VMatrix), // FIELD_VMATRIX
sizeof(VMatrix), // FIELD_VMATRIX_WORLDSPACE
sizeof(matrix3x4_t),// FIELD_MATRIX3X4_WORLDSPACE // NOTE: Use array(FIELD_FLOAT, 12) for matrix3x4_t NOT in worldspace
sizeof(interval_t), // FIELD_INTERVAL
sizeof(int), // FIELD_MODELINDEX
};
#define PREDICTIONCOPY_APPLY( func, nFieldType, pCurrentMap, pField, pOutputData, pInputData, fieldSize ) \
switch( nFieldType ) \ { \ case FIELD_EMBEDDED: \ { \ Error( "FIELD_EMBEDDED in flat list!!!" ); \ } \ break; \ case FIELD_FLOAT: \ func( pCurrentMap, pField, (float *)pOutputData, (const float *)pInputData, fieldSize ); \ break; \ case FIELD_STRING: \ func( pCurrentMap, pField, (char *)pOutputData, (const char *)pInputData, fieldSize ); \ break; \ case FIELD_VECTOR: \ func( pCurrentMap, pField, (Vector *)pOutputData, (const Vector *)pInputData, fieldSize ); \ break; \ case FIELD_QUATERNION: \ func( pCurrentMap, pField, (Quaternion *)pOutputData, (const Quaternion *)pInputData, fieldSize );\ break; \ case FIELD_COLOR32: \ func( pCurrentMap, pField, (color32 *)pOutputData, (const color32 *)pInputData, fieldSize ); \ break; \ case FIELD_BOOLEAN: \ func( pCurrentMap, pField, (bool *)pOutputData, (const bool *)pInputData, fieldSize ); \ break; \ case FIELD_INTEGER: \ func( pCurrentMap, pField, (int *)pOutputData, (const int *)pInputData, fieldSize ); \ break; \ case FIELD_SHORT: \ func( pCurrentMap, pField, (short *)pOutputData, (const short *)pInputData, fieldSize ); \ break; \ case FIELD_CHARACTER: \ func( pCurrentMap, pField, (uint8 *)pOutputData, (const uint8 *)pInputData, fieldSize ); \ break; \ case FIELD_EHANDLE: \ func( pCurrentMap, pField, (EHANDLE *)pOutputData, (const EHANDLE *)pInputData, fieldSize );\ break; \ default: \ break; \ }
#define PREDICTIONCOPY_APPLY_NOEMBEDDED( func, nFieldType, pCurrentMap, pField, pOutputData, pInputData, fieldSize ) \
switch( nFieldType ) \ { \ case FIELD_FLOAT: \ func( pCurrentMap, pField, (float *)pOutputData, (const float *)pInputData, fieldSize ); \ break; \ case FIELD_STRING: \ func( pCurrentMap, pField, (char *)pOutputData, (const char *)pInputData, fieldSize ); \ break; \ case FIELD_VECTOR: \ func( pCurrentMap, pField, (Vector *)pOutputData, (const Vector *)pInputData, fieldSize ); \ break; \ case FIELD_QUATERNION: \ func( pCurrentMap, pField, (Quaternion *)pOutputData, (const Quaternion *)pInputData, fieldSize );\ break; \ case FIELD_COLOR32: \ func( pCurrentMap, pField, (color32 *)pOutputData, (const color32 *)pInputData, fieldSize ); \ break; \ case FIELD_BOOLEAN: \ func( pCurrentMap, pField, (bool *)pOutputData, (const bool *)pInputData, fieldSize ); \ break; \ case FIELD_INTEGER: \ func( pCurrentMap, pField, (int *)pOutputData, (const int *)pInputData, fieldSize ); \ break; \ case FIELD_SHORT: \ func( pCurrentMap, pField, (short *)pOutputData, (const short *)pInputData, fieldSize ); \ break; \ case FIELD_CHARACTER: \ func( pCurrentMap, pField, (uint8 *)pOutputData, (const uint8 *)pInputData, fieldSize ); \ break; \ case FIELD_EHANDLE: \ func( pCurrentMap, pField, (EHANDLE *)pOutputData, (const EHANDLE *)pInputData, fieldSize );\ break; \ default: \ break; \ }
CPredictionCopy::CPredictionCopy( int type, byte *dest, bool dest_packed, const byte *src, bool src_packed, optype_t opType, FN_FIELD_COMPARE func /*= NULL*/ ) { m_OpType = opType; m_nType = type; m_pDest = dest; m_pSrc = src; m_nDestOffsetIndex = dest_packed ? TD_OFFSET_PACKED : TD_OFFSET_NORMAL; m_nSrcOffsetIndex = src_packed ? TD_OFFSET_PACKED : TD_OFFSET_NORMAL;
m_nErrorCount = 0; m_nEntIndex = -1;
m_pWatchField = NULL; m_FieldCompareFunc = func; }
static ConVar cl_pred_error_verbose( "cl_pred_error_verbose", "0", 0, "Show more field info when spewing prediction errors." );
template< class T > inline void CPredictionCopy::CopyField( difftype_t difftype, T *outvalue, const T *invalue, int count ) { for ( int i = 0; i < count; ++i ) { outvalue[ i ] = invalue[ i ]; } }
// specialized for strings
template<> inline void CPredictionCopy::CopyField( difftype_t difftype, char *outvalue, const char *invalue, int count ) { Q_strcpy( outvalue, invalue ); }
template< class T > inline void CPredictionCopy::WatchField( const typedescription_t *pField, const T *outvalue, int count ) { Assert( 0 ); }
// Short
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const short *outvalue, int count ) { WatchMsg( pField, "short (%i)", (int)(outvalue[0]) ); }
// Int
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const int *outvalue, int count ) { bool described = false; if ( pField->flags & FTYPEDESC_MODELINDEX ) { int modelindex = outvalue[0]; model_t const *m = modelinfo->GetModel( modelindex ); if ( m ) { described = true; char shortfile[ 512 ]; shortfile[ 0 ] = 0; Q_FileBase( modelinfo->GetModelName( m ), shortfile, sizeof( shortfile ) );
WatchMsg( pField, "integer (%i->%s)", outvalue[0], shortfile ); } }
if ( !described ) { WatchMsg( pField, "integer (%i)", outvalue[0] ); } }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const bool *outvalue, int count ) { WatchMsg( pField, "bool (%s)", (outvalue[0]) ? "true" : "false" ); }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const float *outvalue, int count ) { WatchMsg( pField, "float (%f)", outvalue[ 0 ] ); }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const char *outstring, int count ) { WatchMsg( pField, "string (%s)", outstring ); }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const Vector* outValue, int count ) { WatchMsg( pField, "vector (%f %f %f)", outValue[0].x, outValue[0].y, outValue[0].z ); }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const Quaternion* outValue, int count ) { WatchMsg( pField, "quaternion (%f %f %f %f)", outValue[0].x, outValue[0].y, outValue[0].z, outValue[0].w ); }
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const EHANDLE *outvalue, int count ) { C_BaseEntity *ent = outvalue[0].Get(); if ( ent ) { const char *classname = ent->GetClassname(); if ( !classname[0] ) { classname = typeid( *ent ).name(); }
WatchMsg( pField, "EHandle (0x%p->%s)", (void *)outvalue[ 0 ], classname ); } else { WatchMsg( pField, "EHandle (NULL)" ); } }
void CPredictionCopy::DumpWatchField( const typedescription_t *pField, const byte *outvalue, int count ) { switch ( pField->fieldType ) { case FIELD_FLOAT: WatchField( pField, (const float *)outvalue, count ); break; case FIELD_STRING: WatchField( pField, (const char *)outvalue, count ); break; case FIELD_VECTOR: WatchField( pField, (const Vector *)outvalue, count ); break; case FIELD_QUATERNION: WatchField( pField, (const Quaternion *)outvalue, count ); break; case FIELD_COLOR32: WatchField( pField, (const color32 *)outvalue, count ); break; case FIELD_BOOLEAN: WatchField( pField, (const bool *)outvalue, count ); break; case FIELD_INTEGER: WatchField( pField, (const int *)outvalue, count ); break; case FIELD_SHORT: WatchField( pField, (const short *)outvalue, count ); break; case FIELD_CHARACTER: WatchField( pField, (const uint8 *)outvalue, count ); break; case FIELD_EHANDLE: WatchField( pField, (const EHANDLE *)outvalue, count ); break; default: break; } }
// color32
template<> inline void CPredictionCopy::WatchField( const typedescription_t *pField, const color32 *outvalue, int count ) { WatchMsg( pField, "color32 (%d %d %d %d)", outvalue[0].r, outvalue[0].g, outvalue[0].b, outvalue[0].a ); }
inline bool QuaternionCompare( const Quaternion& q1, const Quaternion& q2 ) { for ( int i = 0; i < 4; ++i ) { if ( q1[i] != q2[i] ) return false; } return true; }
template< class T > inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const T *outvalue, const T *invalue, int count ) { for ( int i = 0; i < count; i++ ) { if ( outvalue[ i ] == invalue[ i ] ) continue; return DIFFERS; } return IDENTICAL; }
// float uses tolerance
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const float *outvalue, const float *invalue, int count ) { difftype_t retval = IDENTICAL;
float tolerance = pField->fieldTolerance; Assert( tolerance >= 0.0f ); bool usetolerance = tolerance > 0.0f;
if ( usetolerance ) { for ( int i = 0; i < count; ++i ) { float diff = fabs( outvalue[ i ] - invalue[ i ] ); if ( diff <= tolerance ) { retval = WITHINTOLERANCE; continue; }
return DIFFERS; } } else { for ( int i = 0; i < count; ++i ) { if ( outvalue[ i ] == invalue[ i ] ) continue; return DIFFERS; } } return retval; }
// vector uses tolerance
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const Vector *outvalue, const Vector *invalue, int count ) { difftype_t retval = IDENTICAL;
float tolerance = pField->fieldTolerance; Assert( tolerance >= 0.0f ); bool usetolerance = tolerance > 0.0f;
if ( usetolerance ) { for ( int i = 0; i < count; ++i ) { Vector delta = outvalue[ i ] - invalue[ i ];
if ( fabs( delta.x ) <= tolerance && fabs( delta.y ) <= tolerance && fabs( delta.z ) <= tolerance ) { retval = WITHINTOLERANCE; continue; }
return DIFFERS; } } else { for ( int i = 0; i < count; ++i ) { if ( outvalue[ i ] == invalue[ i ] ) continue; return DIFFERS; } } return retval; }
// quaternion uses tolerance
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const Quaternion *outvalue, const Quaternion *invalue, int count ) { difftype_t retval = IDENTICAL;
float tolerance = pField->fieldTolerance; Assert( tolerance >= 0.0f ); bool usetolerance = tolerance > 0.0f;
if ( usetolerance ) { for ( int i = 0; i < count; ++i ) { Quaternion delta; for ( int j = 0; j < 4; j++ ) { delta[i] = outvalue[i][j] - invalue[i][j]; }
if ( fabs( delta.x ) <= tolerance && fabs( delta.y ) <= tolerance && fabs( delta.z ) <= tolerance && fabs( delta.w ) <= tolerance ) { retval = WITHINTOLERANCE; continue; }
return DIFFERS; } } else { for ( int i = 0; i < count; ++i ) { if ( QuaternionCompare( outvalue[ i ], invalue[ i ] ) ) continue; return DIFFERS; } } return retval; }
// string
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const char *outvalue, const char *invalue, int count ) { if ( Q_strcmp( outvalue, invalue ) ) { return DIFFERS; } return IDENTICAL; }
// ehandle
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const EHANDLE *outvalue, const EHANDLE *invalue, int count ) { for ( int i = 0; i < count; i++ ) { if ( outvalue[ i ].Get() == invalue[ i ].Get() ) continue; return DIFFERS; } return IDENTICAL; }
// color32
template<> inline CPredictionCopy::difftype_t CPredictionCopy::CompareField( const typedescription_t *pField, const color32 *outvalue, const color32 *invalue, int count ) { for ( int i = 0; i < count; i++ ) { if ( outvalue[ i ] != invalue[ i ] ) return DIFFERS; } return IDENTICAL; }
template< class T > inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const T *outvalue, const T *invalue, int count ) { Assert( 0 ); }
// short
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const short *outvalue, const short *invalue, int count ) { if ( difftype == DIFFERS ) { int i = 0; ReportFieldsDiffer( pCurrentMap, pField, "short differs (net %i pred %i) diff(%i)\n", (int)(invalue[i]), (int)(outvalue[i]), (int)(outvalue[i] - invalue[i]) ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "short (%i)\n", (int)(outvalue[0]) ); }
// int
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const int *outvalue, const int *invalue, int count ) { if ( difftype == DIFFERS ) { int i = 0; ReportFieldsDiffer( pCurrentMap, pField, "int differs (net %i pred %i) diff(%i)\n", invalue[i], outvalue[i], outvalue[i] - invalue[i] ); }
bool described = false; if ( pField->flags & FTYPEDESC_MODELINDEX ) { int modelindex = outvalue[0]; model_t const *m = modelinfo->GetModel( modelindex ); if ( m ) { described = true; char shortfile[ 512 ]; shortfile[ 0 ] = 0; Q_FileBase( modelinfo->GetModelName( m ), shortfile, sizeof( shortfile ) );
OutputFieldDescription( pCurrentMap, pField, difftype, "integer (%i->%s)\n", outvalue[0], shortfile ); } }
if ( !described ) { OutputFieldDescription( pCurrentMap, pField, difftype, "integer (%i)\n", outvalue[0] ); } }
// bool
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const bool *outvalue, const bool *invalue, int count ) { if ( difftype == DIFFERS ) { int i = 0; ReportFieldsDiffer( pCurrentMap, pField, "bool differs (net %s pred %s)\n", (invalue[i]) ? "true" : "false", (outvalue[i]) ? "true" : "false" ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "bool (%s)\n", (outvalue[0]) ? "true" : "false" ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const float *outvalue, const float *invalue, int count ) { if ( difftype == DIFFERS ) { int i = 0; ReportFieldsDiffer( pCurrentMap, pField, "float differs (net %f pred %f) diff(%f)\n", invalue[ i ], outvalue[ i ], outvalue[ i ] - invalue[ i ] ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "float (%f)\n", outvalue[ 0 ] ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const char *outstring, const char *instring, int count ) { if ( difftype == DIFFERS ) { ReportFieldsDiffer( pCurrentMap, pField, "string differs (net %s pred %s)\n", instring, outstring ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "string (%s)\n", outstring ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const Vector *outValue, const Vector *inValue, int count ) { if ( difftype == DIFFERS ) { int i = 0; Vector delta = outValue[ i ] - inValue[ i ]; ReportFieldsDiffer( pCurrentMap, pField, "vec[] differs (1st diff) (net %f %f %f - pred %f %f %f) delta(%f %f %f)\n", inValue[i].x, inValue[i].y, inValue[i].z, outValue[i].x, outValue[i].y, outValue[i].z, delta.x, delta.y, delta.z ); } OutputFieldDescription( pCurrentMap, pField, difftype, "vector (%f %f %f)\n", outValue[0].x, outValue[0].y, outValue[0].z ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const Quaternion *outValue, const Quaternion *inValue, int count ) { if ( difftype == DIFFERS ) { int i = 0; Quaternion delta; for ( int j = 0; j < 4; j++ ) { delta[i] = outValue[i][j] - inValue[i][j]; }
ReportFieldsDiffer( pCurrentMap, pField, "quaternion[] differs (1st diff) (net %f %f %f %f - pred %f %f %f %f) delta(%f %f %f %f)\n", inValue[i].x, inValue[i].y, inValue[i].z, inValue[i].w, outValue[i].x, outValue[i].y, outValue[i].z, outValue[i].w, delta[0], delta[1], delta[2], delta[3] ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "quaternion (%f %f %f %f)\n", outValue[0][0], outValue[0][1], outValue[0][2], outValue[0][3] ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const EHANDLE *outvalue, EHANDLE const *invalue, int count ) { if ( difftype == DIFFERS ) { int i = 0; ReportFieldsDiffer( pCurrentMap, pField, "EHandles differ (net) 0x%p (pred) 0x%p\n", (void const *)invalue[ i ].Get(), (void *)outvalue[ i ].Get() ); }
C_BaseEntity *ent = outvalue[0].Get(); if ( ent ) { const char *classname = ent->GetClassname(); if ( !classname[0] ) { classname = typeid( *ent ).name(); }
OutputFieldDescription( pCurrentMap, pField, difftype, "EHandle (0x%p->%s)", (void *)outvalue[ 0 ], classname ); } else { OutputFieldDescription( pCurrentMap, pField, difftype, "EHandle (NULL)" ); } }
// color32
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const color32 *outvalue, const color32 *invalue, int count ) { if ( difftype == DIFFERS ) { ReportFieldsDiffer( pCurrentMap, pField, "color differs (net %d %d %d %d pred %d %d %d %d)\n", outvalue[ 0 ].r, outvalue[ 0 ].g, outvalue[ 0 ].b, outvalue[ 0 ].a, invalue[ 0 ].r, invalue[ 0 ].g, invalue[ 0 ].b, invalue[ 0 ].a ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "color (%d %d %d %d)\n", outvalue[ 0 ].r, outvalue[ 0 ].g, outvalue[ 0 ].b, outvalue[ 0 ].a ); }
template<> inline void CPredictionCopy::DescribeField( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t difftype, const uint8 *outstring, const uint8 *instring, int count ) { if ( difftype == DIFFERS ) { ReportFieldsDiffer( pCurrentMap, pField, "byte differs (net %d pred %d)\n", int(instring[0]), int(outstring[0]) ); }
OutputFieldDescription( pCurrentMap, pField, difftype, "byte (%d)\n", int(outstring[0]) ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CPredictionCopy::ReportFieldsDiffer( const datamap_t *pCurrentMap, const typedescription_t *pField, const char *fmt, ... ) { ++m_nErrorCount;
if ( m_FieldCompareFunc ) return;
const char *fieldname = "empty"; const char *classname = "empty"; int flags = 0;
if ( pField ) { flags = pField->flags; fieldname = pField->fieldName ? pField->fieldName : "NULL"; classname = pCurrentMap->dataClassName; }
va_list argptr; char data[ 4096 ]; int len; va_start(argptr, fmt); len = Q_vsnprintf(data, sizeof( data ), fmt, argptr); va_end(argptr);
bool bUseLongName = cl_pred_error_verbose.GetBool(); CUtlString longName; for ( int i = 0; i < m_FieldStack.Count(); ++i ) { const typedescription_t *top = m_FieldStack[ i ]; if ( !top ) continue;
if ( top->flags & FTYPEDESC_KEY ) bUseLongName = true;
longName += top->fieldName ? top->fieldName : "NULL"; longName += "/"; } if ( bUseLongName ) { Msg( "%2d (%d)%s%s::%s - %s", m_nErrorCount, m_nEntIndex, longName.String(), classname, fieldname, data ); } else { Msg( "%2d (%d)%s::%s - %s", m_nErrorCount, m_nEntIndex, classname, fieldname, data ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CPredictionCopy::OutputFieldDescription( const datamap_t *pCurrentMap, const typedescription_t *pField, difftype_t dt, const char *fmt, ... ) { if ( !m_FieldCompareFunc ) return;
const char *fieldname = "empty"; const char *classname = "empty"; int flags = 0;
if ( pField ) { flags = pField->flags; fieldname = pField->fieldName ? pField->fieldName : "NULL"; classname = pCurrentMap->dataClassName; }
va_list argptr; char data[ 4096 ]; int len; va_start(argptr, fmt); len = Q_vsnprintf(data, sizeof( data ), fmt, argptr); va_end(argptr);
bool isnetworked = ( flags & FTYPEDESC_INSENDTABLE ) ? true : false; bool isnoterrorchecked = ( flags & FTYPEDESC_NOERRORCHECK ) ? true : false;
( *m_FieldCompareFunc )( classname, fieldname, g_FieldTypes[ pField->fieldType ], isnetworked, isnoterrorchecked, dt != IDENTICAL ? true : false, dt == WITHINTOLERANCE ? true : false, data ); }
void CPredictionCopy::DescribeFields( const CUtlVector< const datamap_t * > &vecGroups, const datamap_t *pCurrentMap, int nPredictionCopyType ) { int i; int flags; int fieldOffsetSrc; int fieldOffsetDest; int fieldSize;
const flattenedoffsets_t &flat = pCurrentMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; int fieldCount = flat.m_Flattened.Count(); const typedescription_t * RESTRICT pField = &flat.m_Flattened[ 0 ]; PREFETCH360(pField, 0);
for ( i = 0; i < fieldCount; ++i, pField++ ) { #ifdef _GAMECONSOLE
if ( !(i & 0xF) ) { PREFETCH360(pField, 128); } #endif
flags = pField->flags; int nFieldType = pField->fieldType;
const byte * RESTRICT pOutputData; const byte * RESTRICT pInputData;
fieldOffsetDest = pField->flatOffset[ m_nSrcOffsetIndex ]; fieldOffsetSrc = pField->flatOffset[ m_nDestOffsetIndex ]; fieldSize = pField->fieldSize;
pOutputData = (m_pDest + fieldOffsetDest ); pInputData = (m_pSrc + fieldOffsetSrc );
const datamap_t *sourceGroup = pCurrentMap; if ( pField->flatGroup < vecGroups.Count() ) { sourceGroup = vecGroups[ pField->flatGroup ]; }
PREDICTIONCOPY_APPLY( ProcessField_Describe, nFieldType, sourceGroup, pField, pOutputData, pInputData, fieldSize ); } }
//-----------------------------------------------------------------------------
// Purpose: static method
// Input : *fieldname -
// *dmap -
// Output : typedescription_t
//-----------------------------------------------------------------------------
// static method
const typedescription_t *CPredictionCopy::FindFlatFieldByName( const char *fieldname, const datamap_t *dmap ) { PrepareDataMap( const_cast< datamap_t * >( dmap ) );
for ( int i = 0; i < PC_COPYTYPE_COUNT; ++i ) { const flattenedoffsets_t &flat = dmap->m_pOptimizedDataMap->m_Info[ i ].m_Flat; int c = flat.m_Flattened.Count(); for ( int j = 0; j < c; ++j ) { const typedescription_t *td = &flat.m_Flattened[ j ]; if ( !Q_stricmp( td->fieldName, fieldname ) ) { return td; } } } return NULL; }
static ConVar pwatchent( "pwatchent", "-1", FCVAR_CHEAT, "Entity to watch for prediction system changes." ); static ConVar pwatchvar( "pwatchvar", "", FCVAR_CHEAT, "Entity variable to watch in prediction system for changes." );
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CPredictionCopy::WatchMsg( const typedescription_t *pField, const char *fmt, ... ) { Assert( pField ); Assert( m_pOperation );
va_list argptr; char data[ 4096 ]; int len; va_start(argptr, fmt); len = Q_vsnprintf(data, sizeof( data ), fmt, argptr); va_end(argptr);
Msg( "%i %s %s : %s\n", gpGlobals->tickcount, m_pOperation, pField->fieldName, data ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *operation -
// entindex -
// *dmap -
//-----------------------------------------------------------------------------
void CPredictionCopy::DetermineWatchField( const char *operation, int entindex, const datamap_t *dmap ) { m_pWatchField = NULL; m_pOperation = operation; if ( !m_pOperation || !m_pOperation[0] ) return;
int enttowatch = pwatchent.GetInt(); if ( enttowatch < 0 ) return;
if ( entindex != enttowatch ) return;
// See if they specified a field
if ( pwatchvar.GetString()[0] == 0 ) return;
m_pWatchField = CPredictionCopy::FindFlatFieldByName( pwatchvar.GetString(), dmap ); }
static void RemoveFieldsByName( char const *pchFieldName, CUtlVector< typedescription_t > &build ) { // Don't start at final field, since it the one we just added with FTYPEDESC_OVERRIDE
for ( int i = build.Count() - 2; i >= 0 ; --i ) { // Embedded field "offsets" can be the same as their first data field, so don't remove
if ( build[ i ].fieldType == FIELD_EMBEDDED ) continue;
if ( !Q_stricmp( build[ i ].fieldName, pchFieldName ) ) { // Msg( "Removing %s %d due to override\n", build[ i ]->fieldName, build[ i ]->flatOffset[ TD_OFFSET_NORMAL ] );
build.Remove( i ); } } }
static void BuildGroupList_R( int nPredictionCopyType, int nGroup, const datamap_t *dmap, CUtlVector< const datamap_t * > &vecGroups ) { // Descend to base classes first so FTYPEDESC_OVERRIDE can remove the previous ones
if ( dmap->baseMap ) { BuildGroupList_R( nPredictionCopyType, nGroup + 1, dmap->baseMap, vecGroups ); }
vecGroups.AddToTail( dmap ); }
static void BuildFlattenedChains_R( int nPredictionCopyType, int &nMaxGroupSeen, int nGroup, datamap_t *dmap, CUtlVector< typedescription_t > &build, int nBaseOffset ) { // Descend to base classes first so FTYPEDESC_OVERRIDE can remove the previous ones
if ( dmap->baseMap ) { BuildFlattenedChains_R( nPredictionCopyType, nMaxGroupSeen, nGroup + 1, dmap->baseMap, build, nBaseOffset ); }
if ( nGroup > nMaxGroupSeen ) { nMaxGroupSeen = nGroup; }
int c = dmap->dataNumFields; for ( int i = 0; i < c; i++ ) { typedescription_t *pField = &dmap->dataDesc[ i ]; if ( pField->fieldType == FIELD_VOID ) continue;
bool bAdd = true; if ( pField->fieldType != FIELD_EMBEDDED ) { if ( pField->flags & FTYPEDESC_PRIVATE ) { if ( pField->flags & FTYPEDESC_OVERRIDE ) { // Find previous field targeting same offset
RemoveFieldsByName( pField->fieldName, build ); } continue; }
// For PC_NON_NETWORKED_ONLYs skip any fields that are present in the network send tables
if ( nPredictionCopyType == PC_NON_NETWORKED_ONLY && ( pField->flags & FTYPEDESC_INSENDTABLE ) ) { bAdd = false; } // For PC_NETWORKED_ONLYs skip any fields that are not present in the network send tables
if ( nPredictionCopyType == PC_NETWORKED_ONLY && !( pField->flags & FTYPEDESC_INSENDTABLE ) ) { bAdd = false; } } else { bAdd = false; }
pField->flatGroup = nGroup; pField->flatOffset[ TD_OFFSET_NORMAL ] = nBaseOffset + pField->fieldOffset;
if ( bAdd ) { build.AddToTail( *pField ); }
// Msg( "Visit %s offset %d\n", pField->fieldName, pField->flatOffset[ nPackType ] );
if ( pField->fieldType == FIELD_EMBEDDED ) { AssertFatalMsg( !(pField->flags & FTYPEDESC_PTR ), ( "Prediction copy does not support FTYPEDESC_PTR(%d)", pField->fieldName ) ); BuildFlattenedChains_R( nPredictionCopyType, nMaxGroupSeen, nGroup, pField->td, build, pField->flatOffset[ TD_OFFSET_NORMAL ] ); }
if ( pField->flags & FTYPEDESC_OVERRIDE ) { // Find previous field targeting same offset
RemoveFieldsByName( pField->fieldName, build ); } } }
static int __cdecl CompareFlattenedOffsets( const void *pv1, const void *pv2 ) { const typedescription_t *td1 = (const typedescription_t *)pv1; const typedescription_t *td2 = (const typedescription_t *)pv2; if ( td1->flatOffset[ TD_OFFSET_NORMAL ] < td2->flatOffset[ TD_OFFSET_NORMAL ] ) return -1; else if ( td1->flatOffset[ TD_OFFSET_NORMAL ] > td2->flatOffset[ TD_OFFSET_NORMAL ] ) return 1; if ( td1->flatGroup < td2->flatGroup ) return -1; else if ( td1->flatGroup > td2->flatGroup ) return 1;
return 0; }
static int BuildPackedFlattenedOffsets( int nStartOffset, flattenedoffsets_t &flat ) { int current_position = nStartOffset;
for ( int i = 0; i < flat.m_Flattened.Count(); ++i ) { typedescription_t *field = &flat.m_Flattened[ i ];
switch ( field->fieldType ) { default: case FIELD_MODELINDEX: case FIELD_MODELNAME: case FIELD_SOUNDNAME: case FIELD_TIME: case FIELD_TICK: case FIELD_CUSTOM: case FIELD_CLASSPTR: case FIELD_EDICT: case FIELD_POSITION_VECTOR: case FIELD_FUNCTION: Assert( 0 ); break;
case FIELD_EMBEDDED: { Error( "Not expecting FIELD_EMBEDDED in flattened list (%s)", field->fieldName ); } break;
case FIELD_FLOAT: case FIELD_VECTOR: case FIELD_QUATERNION: case FIELD_INTEGER: case FIELD_EHANDLE: case FIELD_COLOR32: case FIELD_VMATRIX: case FIELD_VECTOR4D: { current_position = ALIGN_VALUE( current_position, 4 ); field->flatOffset[ TD_OFFSET_PACKED ] = current_position; current_position += field->fieldSizeInBytes; } break; case FIELD_SHORT: { current_position = ALIGN_VALUE( current_position, 2 ); field->flatOffset[ TD_OFFSET_PACKED ] = current_position; current_position += field->fieldSizeInBytes; } break; case FIELD_STRING: case FIELD_BOOLEAN: case FIELD_CHARACTER: { field->flatOffset[ TD_OFFSET_PACKED ] = current_position; current_position += field->fieldSizeInBytes; } break; case FIELD_VOID: { // Special case, just skip it
} break; }
// Msg( "%s packed to %d size %d\n", field->fieldName, field->flatOffset[ TD_OFFSET_PACKED ], field->fieldSizeInBytes );
}
flat.m_nPackedStartOffset = nStartOffset; flat.m_nPackedSize = current_position - nStartOffset;
current_position = ALIGN_VALUE( current_position, 4 );
return current_position; }
static void BuildDataRuns( datamap_t *dmap ) { for ( int pc = 0; pc < PC_COPYTYPE_COUNT; ++pc ) { datamapinfo_t &info = dmap->m_pOptimizedDataMap->m_Info[ pc ]; datacopyruns_t *runs = &info.m_CopyRuns;
Assert( !info.m_CopyRuns.m_vecRuns.Count() );
const flattenedoffsets_t &flat = info.m_Flat;
int nRunStartField = 0; int nCurrentRunStartOffset = 0; int nLastFieldEndOffset = 0;
CUtlVector< datarun_t > &vecRuns = runs->m_vecRuns;
int i; for ( i = 0; i < flat.m_Flattened.Count(); ++i ) { const typedescription_t *td = &flat.m_Flattened[ i ]; int offset = td->flatOffset[ TD_OFFSET_NORMAL ]; if ( i == 0 ) { nRunStartField = i; nLastFieldEndOffset = offset; nCurrentRunStartOffset = offset; }
if ( td->fieldType == FIELD_EMBEDDED ) { Assert( 0 ); continue; }
if ( nLastFieldEndOffset != offset ) { datarun_t run; run.m_nStartFlatField = nRunStartField; run.m_nEndFlatField = i - 1; Assert( nCurrentRunStartOffset == flat.m_Flattened[ nRunStartField ].flatOffset[ TD_OFFSET_NORMAL ] ); run.m_nStartOffset[ TD_OFFSET_NORMAL ] = nCurrentRunStartOffset; run.m_nStartOffset[ TD_OFFSET_PACKED ] = flat.m_Flattened[ nRunStartField ].flatOffset[ TD_OFFSET_PACKED ]; run.m_nLength = nLastFieldEndOffset - nCurrentRunStartOffset;
#ifdef _GAMECONSOLE
if ( vecRuns.Count() > 0 ) { for ( int td = 0; td < TD_OFFSET_COUNT; ++td ) { vecRuns[ vecRuns.Count() - 1 ].m_nPrefetchOffset[ td ] = run.m_nStartOffset[ td ]; } } #endif
vecRuns.AddToTail( run );
nRunStartField = i; nCurrentRunStartOffset = offset; } nLastFieldEndOffset = td->flatOffset[ TD_OFFSET_NORMAL ] + td->fieldSizeInBytes; }
// Close off last run
if ( nLastFieldEndOffset != nCurrentRunStartOffset ) { datarun_t run; run.m_nStartFlatField = nRunStartField; run.m_nEndFlatField = i - 1; Assert( nCurrentRunStartOffset == flat.m_Flattened[ nRunStartField ].flatOffset[ TD_OFFSET_NORMAL ] ); run.m_nStartOffset[ TD_OFFSET_NORMAL ] = nCurrentRunStartOffset; run.m_nStartOffset[ TD_OFFSET_PACKED ] = flat.m_Flattened[ nRunStartField ].flatOffset[ TD_OFFSET_PACKED ]; run.m_nLength = nLastFieldEndOffset - nCurrentRunStartOffset;
#ifdef _GAMECONSOLE
if ( vecRuns.Count() > 0 ) { for ( int td = 0; td < TD_OFFSET_COUNT; ++td ) { vecRuns[ vecRuns.Count() - 1 ].m_nPrefetchOffset[ td ] = run.m_nStartOffset[ td ]; } } #endif
vecRuns.AddToTail( run ); } } }
static void BuildFlattenedChains( datamap_t *dmap ) { if ( dmap->m_pOptimizedDataMap ) return; dmap->m_pOptimizedDataMap = g_OptimizedDataMapPool.AllocZero();
int nMaxGroupSeen[ PC_COPYTYPE_COUNT ] = { 0 };
for ( int nPredictionCopyType = 0; nPredictionCopyType < PC_COPYTYPE_COUNT; ++nPredictionCopyType ) { CUtlVector< typedescription_t > &build = dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat.m_Flattened;
int nGroupCount = 0; int nBaseOffset = 0; BuildFlattenedChains_R( nPredictionCopyType, nMaxGroupSeen[ nPredictionCopyType ], nGroupCount, dmap, build, nBaseOffset ); // Msg( "%d == %d entries\n", nPredictionCopyType, build[ nPredictionCopyType ].Count() );
}
for ( int nPredictionCopyType = 0; nPredictionCopyType < PC_COPYTYPE_COUNT; ++nPredictionCopyType ) { int nMaxGroup = nMaxGroupSeen[ nPredictionCopyType ];
CUtlVector< typedescription_t > &build = dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat.m_Flattened;
for ( int i = 0; i < build.Count(); ++i ) { typedescription_t *field = &build[ i ]; field->flatGroup = nMaxGroup - field->flatGroup; } }
int nPackedDataStartOffset = 0; for ( int nPredictionCopyType = 0; nPredictionCopyType < PC_COPYTYPE_COUNT; ++nPredictionCopyType ) { flattenedoffsets_t &flat = dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; qsort( flat.m_Flattened.Base(), flat.m_Flattened.Count(), sizeof( typedescription_t ), (int (__cdecl *)(const void *, const void *))CompareFlattenedOffsets );
nPackedDataStartOffset = BuildPackedFlattenedOffsets( nPackedDataStartOffset, flat ); dmap->m_nPackedSize = nPackedDataStartOffset; }
BuildDataRuns( dmap ); }
const tokenset_t< int > s_PredCopyType[] = { { "Non-Sendtable" , PC_NON_NETWORKED_ONLY }, { "SendTable" , PC_NETWORKED_ONLY }, { "Everything" , PC_EVERYTHING }, { NULL, -1 } };
const tokenset_t< int > s_PredPackType[] = { { "Normal" , TD_OFFSET_NORMAL }, { "Packed" , TD_OFFSET_PACKED }, { NULL, -1 } };
static void DescribeRuns( const datamap_t *dmap, int nPredictionCopyType, int packType ) { const flattenedoffsets_t *flat; const datarun_t *run; const datacopyruns_t &runs = dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_CopyRuns; flat = &dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; Msg( " Runs for copy type: %s, packing: %s\n", s_PredCopyType->GetNameByToken( nPredictionCopyType ), s_PredPackType->GetNameByToken( packType ) ); for ( int i = 0; i < runs.m_vecRuns.Count(); ++i ) { run = &runs.m_vecRuns[ i ]; Msg( " %5d: %5d -> %5d (%5d bytes): %s to %s\n", i, run->m_nStartOffset[ packType ], run->m_nStartOffset[ packType ] + run->m_nLength, run->m_nLength, flat->m_Flattened[ run->m_nStartFlatField ].fieldName, flat->m_Flattened[ run->m_nEndFlatField ].fieldName ); } }
static void DescribeFlattenedList( const datamap_t *dmap, int nPredictionCopyType, int packType ) { char const *prefix; prefix = dmap->dataClassName;
Msg( "->Sorted %s for copy type: %s, packing: %s\n", prefix, s_PredCopyType->GetNameByToken( nPredictionCopyType ), s_PredPackType->GetNameByToken( packType ) ); // Now dump the flattened list
int nLastFieldEnd = 0; int nCurrentRunStartOffset = 0;
int nRuns = 0; int nBytesInRuns = 0; int offset = 0;
const flattenedoffsets_t &list = dmap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; for ( int i = 0; i < list.m_Flattened.Count(); ++i ) { const typedescription_t *td = &list.m_Flattened[ i ]; offset = td->flatOffset[ packType ]; if ( i == 0 ) { nLastFieldEnd = offset; nCurrentRunStartOffset = offset; }
if ( td->fieldType != FIELD_EMBEDDED ) { if ( nLastFieldEnd != offset ) { Msg( " gap of %d bytes [last run %d]\n", offset - nLastFieldEnd, ( nLastFieldEnd - nCurrentRunStartOffset ) ); ++nRuns; nBytesInRuns += ( nLastFieldEnd - nCurrentRunStartOffset );
nCurrentRunStartOffset = offset; } nLastFieldEnd = td->flatOffset[ packType ] + td->fieldSizeInBytes; }
Msg( "group %s [flat %d] [sort %d] %d bytes\n", td->fieldName, td->flatOffset[ packType ], td->flatOffset[ TD_OFFSET_NORMAL ], td->fieldSizeInBytes ); }
// Close off last run
if ( nLastFieldEnd != nCurrentRunStartOffset ) { Msg( "Last run %d\n", nLastFieldEnd - nCurrentRunStartOffset ); ++nRuns; nBytesInRuns += ( nLastFieldEnd - nCurrentRunStartOffset ); }
if ( nRuns > 0 ) { Msg( "%d runs, %d bytes in runs, %f avg bytes per run\n", nRuns, nBytesInRuns, nBytesInRuns/(float)nRuns ); } Msg( "->\n" );
DescribeRuns( dmap, nPredictionCopyType, packType ); }
void CPredictionCopy::CopyFlatFieldsUsingRuns( const datamap_t *pCurrentMap, int nPredictionCopyType ) { int fieldOffsetSrc; int fieldOffsetDest; byte * RESTRICT pOutputData; const byte * RESTRICT pInputData;
const datacopyruns_t &runs = pCurrentMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_CopyRuns;
byte * RESTRICT pDest = ( byte * RESTRICT )m_pDest; const byte * RESTRICT pSrc = (const byte * RESTRICT)m_pSrc;
PREFETCH360( pSrc, 0 ); PREFETCH360( pDest, 0 );
int c = runs.m_vecRuns.Count(); for ( int i = 0; i < c; ++i ) { const datarun_t * RESTRICT run = &runs.m_vecRuns[ i ]; fieldOffsetDest = run->m_nStartOffset[ m_nDestOffsetIndex ]; fieldOffsetSrc = run->m_nStartOffset[ m_nSrcOffsetIndex ]; pOutputData = pDest + fieldOffsetDest; pInputData = pSrc + fieldOffsetSrc;
#ifdef _GAMECONSOLE
PREFETCH360( pDest + run->m_nPrefetchOffset[ m_nDestOffsetIndex ], 0 ); PREFETCH360( pSrc + run->m_nPrefetchOffset[ m_nSrcOffsetIndex ], 0 ); #endif
Q_memcpy( pOutputData, pInputData, run->m_nLength ); } }
void CPredictionCopy::CopyFlatFields( const datamap_t *pCurrentMap, int nPredictionCopyType ) { int fieldOffsetSrc; int fieldOffsetDest; byte * RESTRICT pOutputData; const byte * RESTRICT pInputData;
byte * RESTRICT pDest = (byte * RESTRICT)m_pDest; const byte * RESTRICT pSrc = (const byte * RESTRICT)m_pSrc;
PREFETCH360( pSrc, 0 ); PREFETCH360( pDest, 0 );
const flattenedoffsets_t &flat = pCurrentMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat;
int fieldCount = flat.m_Flattened.Count(); for ( int i = 0; i < fieldCount; ++i ) { const typedescription_t *pField = &flat.m_Flattened[ i ];
fieldOffsetDest = pField->flatOffset[ m_nDestOffsetIndex ]; fieldOffsetSrc = pField->flatOffset[ m_nSrcOffsetIndex ]; pOutputData = pDest + fieldOffsetDest; PREFETCH360( pOutputData, 0 ); pInputData = pSrc + fieldOffsetSrc; PREFETCH360( pInputData, 0 );
Q_memcpy( pOutputData, pInputData, pField->fieldSizeInBytes ); } }
void CPredictionCopy::ErrorCheckFlatFields_NoSpew( const datamap_t *pCurrentMap, int nPredictionCopyType ) { int i; int flags; int fieldOffsetSrc; int fieldOffsetDest; int fieldSize;
const flattenedoffsets_t &flat = pCurrentMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; const typedescription_t *pBase = &flat.m_Flattened[ 0 ]; PREFETCH360( pBase, 0 ); int fieldCount = flat.m_Flattened.Count();
const byte * RESTRICT pDest = (const byte * RESTRICT)m_pDest; const byte * RESTRICT pSrc = (const byte * RESTRICT)m_pSrc; const byte *pOutputData; const byte *pInputData;
PREFETCH360( pSrc, 0 ); PREFETCH360( pDest, 0 );
for ( i = 0; i < fieldCount && !m_nErrorCount; ++i ) { const typedescription_t * RESTRICT pField = &pBase[ i ];
flags = pField->flags; if ( flags & FTYPEDESC_NOERRORCHECK ) continue;
#ifdef _GAMECONSOLE
if ( !(i & 0xF) ) { PREFETCH360(pField, 128); } #endif
fieldOffsetDest = pField->flatOffset[ m_nDestOffsetIndex ]; fieldOffsetSrc = pField->flatOffset[ m_nSrcOffsetIndex ];
pOutputData = pDest + fieldOffsetDest; PREFETCH360( pOutputData, 0 ); pInputData = pSrc + fieldOffsetSrc; PREFETCH360( pInputData, 0 );
fieldSize = pField->fieldSize; int nFieldType = pField->fieldType;
PREDICTIONCOPY_APPLY( ProcessField_Compare_NoSpew, nFieldType, pCurrentMap, pField, pOutputData, pInputData, fieldSize ); } }
void CPredictionCopy::ErrorCheckFlatFields_Spew( const datamap_t *pCurrentMap, int nPredictionCopyType ) { int i; int flags; int fieldOffsetSrc; int fieldOffsetDest; int fieldSize;
const flattenedoffsets_t &flat = pCurrentMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; const typedescription_t *pBase = &flat.m_Flattened[ 0 ]; PREFETCH360( pBase, 0 ); int fieldCount = flat.m_Flattened.Count();
const byte * RESTRICT pDest = m_pDest; const byte * RESTRICT pSrc = m_pSrc;
const byte *pOutputData; const byte *pInputData;
PREFETCH360( pSrc, 0 ); PREFETCH360( pDest, 0 );
for ( i = 0; i < fieldCount ; ++i ) { const typedescription_t * RESTRICT pField = &pBase[ i ];
flags = pField->flags; if ( flags & FTYPEDESC_NOERRORCHECK ) continue;
#ifdef _GAMECONSOLE
if ( !(i & 0xF) ) { PREFETCH360(pField, 128); } #endif
fieldOffsetDest = pField->flatOffset[ m_nDestOffsetIndex ]; fieldOffsetSrc = pField->flatOffset[ m_nSrcOffsetIndex ];
pOutputData = pDest + fieldOffsetDest; PREFETCH360( pOutputData, 0 ); pInputData = pSrc + fieldOffsetSrc; PREFETCH360( pInputData, 0 );
fieldSize = pField->fieldSize; flags = pField->flags; int nFieldType = pField->fieldType;
PREDICTIONCOPY_APPLY( ProcessField_Compare_Spew, nFieldType, pCurrentMap, pField, pOutputData, pInputData, fieldSize ); } }
bool CPredictionCopy::PrepareDataMap( datamap_t *dmap ) { bool bPerformedPrepare = false; if ( dmap && !dmap->m_pOptimizedDataMap ) { bPerformedPrepare = true; BuildFlattenedChains( dmap ); dmap = dmap->baseMap; }
return bPerformedPrepare; }
CON_COMMAND( cl_predictioncopy_describe, "Describe datamap_t for entindex" ) { if ( args.ArgC() <= 1 ) { Msg( "Usage: %s <entindex>", args[ 0 ] ); return; } int entindex = Q_atoi( args[ 1 ] ); C_BaseEntity *ent = C_BaseEntity::Instance( entindex ); if ( !ent ) { Msg( "cl_predictioncopy_describe: no such entity %d\n", entindex ); return; }
datamap_t *dmap = ent->GetPredDescMap(); if ( !dmap ) { return; } CPredictionCopy::PrepareDataMap( dmap );
for ( int nPredictionCopyType = 0; nPredictionCopyType < PC_COPYTYPE_COUNT; ++nPredictionCopyType ) { //for ( int i = 0; i < TD_OFFSET_COUNT; ++i )
{ DescribeFlattenedList( dmap, nPredictionCopyType, 0 ); } } }
// 0 PC_NON_NETWORKED_ONLY = (1<<0) or (1)
// 1 PC_NETWORKED_ONLY = (1<<1) or (2)
// 2 PC_EVERYTHING = ( PC_NON_NETWORKED_ONLY | PC_NETWORKED_ONLY ) or 3
// So for these three options, we just take the type and add one!!!
FORCEINLINE int ComputeTypeMask( int nType ) { return nType + 1; }
#if 0 // Enable this for perf testing
static ConVar cl_predictioncopy_runs( "cl_predictioncopy_runs", "1" ); static ConVar cl_predictioncopy_repeats( "cl_predictioncopy_repeats", "1" ); void CPredictionCopy::TransferDataCopyOnly( datamap_t *dmap ) { int repeat = cl_predictioncopy_repeats.GetInt(); for ( int k = 0; k < repeat; ++k ) { int types = ComputeTypeMask( m_nType ); for ( int i = 0; i < PC_COPYTYPE_COUNT; ++i ) { if ( types & (1<<i) ) { if ( !cl_predictioncopy_runs.GetBool() ) { CopyFlatFields( dmap, i ); } else { CopyFlatFieldsUsingRuns( dmap, i ); } } } } } #else
void CPredictionCopy::TransferDataCopyOnly( const datamap_t *dmap ) { int types = ComputeTypeMask( m_nType ); for ( int i = 0; i < PC_COPYTYPE_COUNT; ++i ) { if ( types & (1<<i) ) { CopyFlatFieldsUsingRuns( dmap, i ); } } } #endif
// Stop at first error
void CPredictionCopy::TransferDataErrorCheckNoSpew( char const *pchOperation, const datamap_t *dmap ) { int types = ComputeTypeMask( m_nType ); for ( int i = 0; i < PC_COPYTYPE_COUNT && !m_nErrorCount; ++i ) { if ( types & (1<<i) ) { ErrorCheckFlatFields_NoSpew( dmap, i ); } } }
void CPredictionCopy::TransferDataErrorCheckSpew( char const *pchOperation, const datamap_t *dmap ) { int types = ComputeTypeMask( m_nType ); for ( int i = 0; i < PC_COPYTYPE_COUNT; ++i ) { if ( types & (1<<i) ) { ErrorCheckFlatFields_Spew( dmap, i ); } } }
void CPredictionCopy::TransferDataDescribe( char const *pchOperation, const datamap_t *dmap ) { // Copy from here first, then base classes
int types = ComputeTypeMask( m_nType ); for ( int i = 0; i < PC_COPYTYPE_COUNT; ++i ) { if ( types & (1<<i) ) { CUtlVector< const datamap_t * > vecGroups; int nGroup = 0; BuildGroupList_R( i, nGroup, dmap, vecGroups ); DescribeFields( vecGroups, dmap, i ); } } }
int CPredictionCopy::TransferData( const char *operation, int entindex, datamap_t *dmap ) { m_nEntIndex = entindex;
PrepareDataMap( dmap );
switch ( m_OpType ) { default: Assert( 0 ); case TRANSFERDATA_COPYONLY: // Data copying only (uses runs)
{ // Watch is based on "destination" of any write operation
DetermineWatchField( operation, entindex, dmap ); TransferDataCopyOnly( dmap ); } break; case TRANSFERDATA_ERRORCHECK_NOSPEW: // Checks for errors, returns after first error found
{ TransferDataErrorCheckNoSpew( operation, dmap ); } break; case TRANSFERDATA_ERRORCHECK_SPEW: // checks for errors, reports all errors to console
{ TransferDataErrorCheckSpew( operation, dmap ); } break; case TRANSFERDATA_ERRORCHECK_DESCRIBE: { TransferDataDescribe( operation, dmap ); } break; }
if ( m_pWatchField ) { // Watch is based on "destination" of any write operation
DumpWatchField( m_pWatchField, m_pDest + m_pWatchField->flatOffset[ m_nDestOffsetIndex ], m_pWatchField->fieldSize ); }
return m_nErrorCount; }
#endif
|