csgo-2018-source/game/shared/predictioncopy.cpp
2021-07-24 21:11:47 -07:00

1736 lines
49 KiB
C++

//========= 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