diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/dt_test.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/dt_test.cpp')
| -rw-r--r-- | engine/dt_test.cpp | 1010 |
1 files changed, 1010 insertions, 0 deletions
diff --git a/engine/dt_test.cpp b/engine/dt_test.cpp new file mode 100644 index 0000000..1c8cca8 --- /dev/null +++ b/engine/dt_test.cpp @@ -0,0 +1,1010 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// ---------------------------------------------------------------------------------------- // +// This is a datatable test case. +// It is run in debug mode when the engine starts to catch any bugs in datatable code. +// This can also serve as a simple example of how datatables work separately from the +// intricacies of the entity system. +// ---------------------------------------------------------------------------------------- // +// This is also a good place to test new code since it's run right when the engine +// starts up. It can also be put into standalone apps easily. +// ---------------------------------------------------------------------------------------- // +// Things it tests: +// - Data transmission integrity. +// - Delta calculation. +// - Strings, floats, vectors, recursive datatables, and ints. +// - Fixed-length arrays. +// - Variable-length arrays. +// - Exclude props. +// - Recursive datatables. +// - Datatable proxies returning false. +// - CUtlVectors of regular types (like floats) and data tables. +// ---------------------------------------------------------------------------------------- // +// Things it does not test: +// - Quantization. +// - Clamping. +// - The entity system's usage of data tables. +// - Stress testing - too many properties, maxing out delta bits. +// - Built-in and custom client and server proxies. +// ---------------------------------------------------------------------------------------- // +// At a high level, the test is setup as such: +// - Server structure and datatable. +// - Client structure and datatable. +// - A function table with a function to modify and compare each element. +// - A function that initializes the server structure and tries random changes to it and +// verifies that the client receives the deltas and changes correctly. +// ---------------------------------------------------------------------------------------- // +// Eventually it would be nice to stress-test the entities with tests for: +// - Misordered proxy callbacks and missing data. +// ---------------------------------------------------------------------------------------- // +#include "quakedef.h" +#include "dt.h" +#include "dt_send.h" +#include "dt_recv.h" +#include "tier0/dbg.h" +#include "dt_utlvector_send.h" +#include "dt_utlvector_recv.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// If datatables support these again, then uncomment this to have them tested. +//#define SUPPORT_ARRAYS_OF_DATATABLES + + +#ifdef _DEBUG + + + +class DTTestSub2Sub +{ +public: + int m_Int2; +}; + +class DTTestSub2 +{ +public: + int m_Int; + DTTestSub2Sub m_Sub; +}; + + +class CTestStruct +{ +public: + int a,b; + float f; +}; + + +#define MAX_STRUCTARRAY_ELEMENTS 11 +#define MAX_FLOATARRAY_ELEMENTS 18 +#define MAX_CHARARRAY_ELEMENTS 22 + + +// ------------------------------------------------------------------------------------------- // +// DTTestServerSub and its DataTable. +// ------------------------------------------------------------------------------------------- // +class DTTestServerSub +{ +public: + float m_FloatArray[3]; + char m_Strings[2][64]; + + CUtlVector<CTestStruct> m_UtlVectorStruct; + CUtlVector<float> m_UtlVectorFloat; + CUtlVector<char> m_UtlVectorChar; +}; + +void SendProxy_DTTestServerSubString( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) +{ + SendProxy_StringToString( pProp, pStruct, pData, pOut, iElement, objectID); + +} + +BEGIN_SEND_TABLE_NOBASE( CTestStruct, DT_TestStruct ) + SendPropInt( SENDINFO_NOCHECK( a ) ), + SendPropInt( SENDINFO_NOCHECK( b ) ), + SendPropFloat( SENDINFO_NOCHECK( f ) ) +END_SEND_TABLE() + +BEGIN_SEND_TABLE_NOBASE(DTTestServerSub, DT_DTTestSub) + // - Auto type conversions (receiving an array of floats into an array of ints). + SendPropArray( + SendPropFloat(SENDINFO_NOCHECK(m_FloatArray[0]), 0, SPROP_NOSCALE), + m_FloatArray), + + SendPropUtlVectorDataTable( m_UtlVectorStruct, MAX_STRUCTARRAY_ELEMENTS, DT_TestStruct ), + + SendPropArray( + SendPropString(SENDINFO_NOCHECK(m_Strings[0]), 0, SendProxy_DTTestServerSubString), + m_Strings ), + + SendPropUtlVector( + SENDINFO_UTLVECTOR( m_UtlVectorChar ), + MAX_CHARARRAY_ELEMENTS, + SendPropInt( NULL, 0, sizeof( char ), 0 ) ), + + SendPropUtlVector( + SENDINFO_UTLVECTOR( m_UtlVectorFloat ), + MAX_FLOATARRAY_ELEMENTS, // max elements + SendPropFloat( NULL, 0, 0, 0, SPROP_NOSCALE ) ) +END_SEND_TABLE() + + + +BEGIN_SEND_TABLE_NOBASE(DTTestSub2Sub, DT_DTTestSub2Sub) + SendPropInt( SENDINFO_NOCHECK( m_Int2 ), 32 ), +END_SEND_TABLE() + +BEGIN_SEND_TABLE_NOBASE(DTTestSub2, DT_DTTestSub2) + SendPropDataTable(SENDINFO_DT(m_Sub), &REFERENCE_SEND_TABLE(DT_DTTestSub2Sub)), + SendPropInt( SENDINFO_NOCHECK( m_Int ), 32 ), +END_SEND_TABLE() + + + +// ------------------------------------------------------------------------------------------- // +// DTTestServer and its DataTable. +// ------------------------------------------------------------------------------------------- // +class DTTestServer +{ +public: + DTTestServerSub m_Sub; + DTTestSub2 m_Sub2; + + float m_Float; + +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + DTTestServerSub m_SubArray[2]; +#endif + + Vector m_Vector; + char m_String[64]; + int m_Int; + int m_IntArray[32]; // Note that the server and client array length are different. + char m_CharArray[8]; + + int m_VLALength; + int m_VLA[16]; +}; + +void SendProxy_DTTestServerFloat( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID ) +{ + SendProxy_FloatToFloat(pProp, pStruct, pData, pOut, iElement, objectID); +} + +void SendProxy_DTTestServerVector( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID ) +{ + SendProxy_VectorToVector(pProp, pStruct, pData, pOut, iElement, objectID); +} + +void SendProxy_DTTestServerString( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID ) +{ + SendProxy_StringToString(pProp, pStruct, pData, pOut, iElement, objectID); +} + +void SendProxy_DTTestServerInt( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID ) +{ + SendProxy_Int32ToInt32(pProp, pStruct, pData, pOut, iElement, objectID); +} + +bool g_bSendSub = true; +void* SendProxy_DTTestServerSub( const SendProp *pProp, const void *pStruct, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) +{ + if( !g_bSendSub ) + return NULL; + + return SendProxy_DataTableToDataTable( pProp, pStruct, pData, pRecipients, objectID ); +} +REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_DTTestServerSub ); + + +int ArrayLengthSendProxy_VLALength( const void *pStruct, int objectID ) +{ + DTTestServer *pServer = (DTTestServer*)pStruct; + return pServer->m_VLALength; +} + + +BEGIN_SEND_TABLE_NOBASE(DTTestServer, DT_DTTest) + SendPropVariableLengthArray( + ArrayLengthSendProxy_VLALength, + SendPropInt( SENDINFO_NOCHECK( m_VLA[0] ) ), + m_VLA ), + + // Test exclude props. + SendPropExclude( "DT_DTTest", "m_Int" ), + SendPropDataTable(SENDINFO_DT(m_Sub), &REFERENCE_SEND_TABLE(DT_DTTestSub), SendProxy_DTTestServerSub), + + + SendPropFloat (SENDINFO_NOCHECK(m_Float), 32, SPROP_NOSCALE), + + SendPropDataTable(SENDINFO_DT(m_Sub2), &REFERENCE_SEND_TABLE(DT_DTTestSub2)), + + SendPropInt (SENDINFO_NOCHECK(m_Int), 23, SPROP_UNSIGNED), + + SendPropExclude( "DT_DTTestSub", "m_FloatArray" ), + + + SendPropString(SENDINFO_NOCHECK(m_String)), + + SendPropArray( + SendPropInt(SENDINFO_NOCHECK(m_CharArray[0]), 8), + m_CharArray), + + SendPropArray( + SendPropInt (SENDINFO_NOCHECK(m_IntArray[0]), 23, SPROP_UNSIGNED), + m_IntArray), + +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + SendPropArray( + SendPropDataTable(SENDINFO_DT(m_SubArray[0]), &REFERENCE_SEND_TABLE(DT_DTTestSub), SendProxy_DTTestServerSub), + m_SubArray ), +#endif + + SendPropVector(SENDINFO_NOCHECK(m_Vector), 32, SPROP_NOSCALE) +END_SEND_TABLE() + + + +// ------------------------------------------------------------------------------------------- // +// DTTestClientSub and its DataTable. +// ------------------------------------------------------------------------------------------- // +class DTTestClientSub +{ +public: + char m_Strings[2][64]; + float m_FloatArray[3]; + + CUtlVector<CTestStruct> m_UtlVectorStruct; + CUtlVector<float> m_UtlVectorFloat; + CUtlVector<char> m_UtlVectorChar; +}; + +void RecvProxy_DTTestClientSubString( const CRecvProxyData *pData, void *pStruct, void *pOut ) +{ + RecvProxy_StringToString( pData, pStruct, pOut ); +} + +BEGIN_RECV_TABLE_NOBASE( CTestStruct, DT_TestStruct ) + RecvPropInt( RECVINFO( a ) ), + RecvPropInt( RECVINFO( b ) ), + RecvPropFloat( RECVINFO( f ) ), +END_RECV_TABLE() + +BEGIN_RECV_TABLE_NOBASE(DTTestClientSub, DT_DTTestSub) + // - Auto type conversions (receiving an array of floats into an array of ints). + RecvPropArray( + RecvPropFloat(RECVINFO(m_FloatArray[0])), + m_FloatArray), + + RecvPropUtlVector( RECVINFO_UTLVECTOR( m_UtlVectorFloat ), MAX_FLOATARRAY_ELEMENTS, RecvPropFloat(NULL,0,0) ), + RecvPropUtlVectorDataTable( m_UtlVectorStruct, MAX_STRUCTARRAY_ELEMENTS, DT_TestStruct ), + + RecvPropUtlVector( + RECVINFO_UTLVECTOR( m_UtlVectorChar ), + MAX_CHARARRAY_ELEMENTS, + RecvPropInt( NULL, 0, sizeof( char ) ) ), + + RecvPropArray( + RecvPropString(RECVINFO(m_Strings[0]), 0, RecvProxy_DTTestClientSubString), + m_Strings), +END_RECV_TABLE() + +BEGIN_RECV_TABLE_NOBASE(DTTestSub2Sub, DT_DTTestSub2Sub) + RecvPropInt( RECVINFO( m_Int2 ), 32 ), +END_RECV_TABLE() + +BEGIN_RECV_TABLE_NOBASE(DTTestSub2, DT_DTTestSub2) + RecvPropDataTable(RECVINFO_DT(m_Sub), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub2Sub)), + RecvPropInt( RECVINFO( m_Int ) ), +END_RECV_TABLE() + + + + +// ------------------------------------------------------------------------------------------- // +// DTTestClient and DataTable. +// ------------------------------------------------------------------------------------------- // +class DTTestClient +{ +public: + DTTestClientSub m_Sub; + long m_Guard1; + + DTTestSub2 m_Sub2; + long m_Guard2; + +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + DTTestClientSub m_SubArray[2]; +#endif + + long m_Guard3; + + float m_Float; + long m_Guard4; + + Vector m_Vector; + long m_Guard5; + + char m_String[64]; + long m_Guard6; + + int m_Int; + long m_Guard7; + + int m_IntArray[32]; // Note that the server and client array length are different. + long m_Guard8; + + char m_CharArray[8]; + long m_Guard9; + + int m_VLALength; + int m_VLA[16]; +}; + + +void RecvProxyArrayLength_VLA( void *pStruct, int objectID, int currentArrayLength ) +{ + DTTestClient *pClient = (DTTestClient*)pStruct; + pClient->m_VLALength = currentArrayLength; +} + + +BEGIN_RECV_TABLE_NOBASE(DTTestClient, DT_DTTest) + +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + RecvPropArray( + RecvPropDataTable(RECVINFO_DT(m_SubArray[0]), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub), RecvProxy_DTTestClientSub), + m_SubArray ), +#endif + + RecvPropFloat (RECVINFO(m_Float), 0), + + RecvPropDataTable(RECVINFO_DT(m_Sub), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub)), + RecvPropDataTable(RECVINFO_DT(m_Sub2), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub2)), + + // - Arrays with and without the SPROP_ONEBITDELTA flag. + RecvPropArray( + RecvPropInt (RECVINFO(m_CharArray[0]), 8), + m_CharArray), + + RecvPropVector(RECVINFO(m_Vector), 0), + RecvPropString(RECVINFO_STRING(m_String), 0), + + RecvPropInt (RECVINFO(m_Int), 0), + + // - Arrays with and without the SPROP_ONEBITDELTA flag. + // - Array size mismatches between the client and the server. + RecvPropArray( + RecvPropInt (RECVINFO(m_IntArray[0]), 0), + m_IntArray), + + RecvPropInt( RECVINFO( m_VLALength ) ), + + RecvPropVariableLengthArray( + RecvProxyArrayLength_VLA, + RecvPropInt( RECVINFO( m_VLA[0] ) ), + m_VLA ) +END_RECV_TABLE() + + + + +// ------------------------------------------------------------------------------------------- // +// Functions that act on the data. +// ------------------------------------------------------------------------------------------- // + +typedef bool (*CompareElementFn)(DTTestClient *pClient, DTTestServer *pServer); +typedef void (*RandomlyChangeElementFn)(DTTestServer *pServer); + +float FRand(double minVal, double maxVal) +{ + return (float)(((double)rand() / VALVE_RAND_MAX) * (maxVal - minVal) + minVal); +} + +void RandomlyChangeStringGeneric(char *str, int size) +{ + for(int i=0; i < size-1; i++) + str[i] = (char)rand(); + + str[size-1] = 0; +} + +bool CompareTestSubString0(DTTestClient *pClient, DTTestServer *pServer) +{ + return strcmp(pClient->m_Sub.m_Strings[0], pServer->m_Sub.m_Strings[0]) == 0; +} +void RandomlyChangeSubString0(DTTestServer *pServer) +{ + if( g_bSendSub ) + RandomlyChangeStringGeneric(pServer->m_Sub.m_Strings[0], sizeof(pServer->m_Sub.m_Strings[0])); +} + +bool CompareTestSubString1(DTTestClient *pClient, DTTestServer *pServer) +{ + return strcmp(pClient->m_Sub.m_Strings[1], pServer->m_Sub.m_Strings[1]) == 0; +} +void RandomlyChangeSubString1(DTTestServer *pServer) +{ + if( g_bSendSub ) + RandomlyChangeStringGeneric(pServer->m_Sub.m_Strings[1], sizeof(pServer->m_Sub.m_Strings[1])); +} + +bool CompareFloat(DTTestClient *pClient, DTTestServer *pServer) +{ + return pClient->m_Float == pServer->m_Float; +} +void RandomlyChangeFloat(DTTestServer *pServer) +{ + pServer->m_Float = FRand(-500000, 500000); +} + +bool CompareVector(DTTestClient *pClient, DTTestServer *pServer) +{ + return pClient->m_Vector.x == pServer->m_Vector.x && + pClient->m_Vector.y == pServer->m_Vector.y && + pClient->m_Vector.z == pServer->m_Vector.z; +} +void RandomlyChangeVector(DTTestServer *pServer) +{ + pServer->m_Vector.x = FRand(-500000, 500000); + pServer->m_Vector.y = FRand(-500000, 500000); + pServer->m_Vector.z = FRand(-500000, 500000); +} + +bool CompareString(DTTestClient *pClient, DTTestServer *pServer) +{ + return strcmp(pClient->m_String, pServer->m_String) == 0; +} +void RandomlyChangeString(DTTestServer *pServer) +{ + //memset( pServer->m_String, , sizeof( pServer->m_String ) ); + Q_strncpy( pServer->m_String, "a", sizeof( pServer->m_String ) ); + + //RandomlyChangeStringGeneric(pServer->m_String, sizeof(pServer->m_String)); +} + +bool CompareInt(DTTestClient *pClient, DTTestServer *pServer) +{ +// (m_Int is the exclude prop we're testing) +// return pClient->m_Int == pServer->m_Int; +return true; +} +void RandomlyChangeInt(DTTestServer *pServer) +{ + pServer->m_Int = (int)rand(); +} + +bool CompareIntArray(DTTestClient *pClient, DTTestServer *pServer) +{ + // Just verify however much of the data we can. + int leastElements = (sizeof(pClient->m_IntArray) < sizeof(pServer->m_IntArray)) ? (sizeof(pClient->m_IntArray)/sizeof(pClient->m_IntArray[0])) : (sizeof(pServer->m_IntArray)/sizeof(pServer->m_IntArray[0])); + return memcmp(pClient->m_IntArray, pServer->m_IntArray, leastElements*sizeof(int)) == 0; +} +void RandomlyChangeIntArray(DTTestServer *pServer) +{ + // Change a random subset of the array. + int nElements = sizeof(pServer->m_IntArray) / sizeof(pServer->m_IntArray[0]); + int nChanges = 4 + rand() % nElements; + + for(int i=0; i < nChanges; i++) + { + pServer->m_IntArray[rand() % nElements] = (int)rand(); + } +} + +bool CompareFloatArray(DTTestClient *pClient, DTTestServer *pServer) +{ +// m_FloatArray is an ExcludeProp. +/* + int leastElements = (sizeof(pClient->m_Sub.m_FloatArray) < sizeof(pServer->m_Sub.m_FloatArray)) ? (sizeof(pClient->m_Sub.m_FloatArray)/sizeof(pClient->m_Sub.m_FloatArray[0])) : (sizeof(pServer->m_Sub.m_FloatArray)/sizeof(pServer->m_Sub.m_FloatArray[0])); + for(int i=0; i < leastElements; i++) + { + if(pClient->m_Sub.m_FloatArray[i] != pServer->m_Sub.m_FloatArray[i]) + return false; + } +*/ + return true; +} +void RandomlyChangeFloatArray(DTTestServer *pServer) +{ + // Change a random subset of the array. + int nElements = sizeof(pServer->m_Sub.m_FloatArray) / sizeof(pServer->m_Sub.m_FloatArray[0]); + int nChanges = 4 + rand() % nElements; + + for(int i=0; i < nChanges; i++) + { + pServer->m_Sub.m_FloatArray[rand() % nElements] = (float)rand() * 0.943123f; + } +} + +bool CompareCharArray(DTTestClient *pClient, DTTestServer *pServer) +{ + return memcmp(pClient->m_CharArray, pServer->m_CharArray, sizeof(pClient->m_CharArray)) == 0; +} +void RandomlyChangeCharArray(DTTestServer *pServer) +{ + for(int i=0; i < (sizeof(pServer->m_CharArray) / sizeof(pServer->m_CharArray[0])); i++) + pServer->m_CharArray[i] = (char)rand(); +} + + +bool CompareSubArray(DTTestClient *pClient, DTTestServer *pServer ) +{ +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + for( int i=0; i < 2; i++ ) + { + for( int z=0; z < sizeof(pServer->m_SubArray[0].m_FloatArray) / sizeof(pServer->m_SubArray[0].m_FloatArray[0]); z++ ) + { + if( pServer->m_SubArray[i].m_FloatArray[z] != pClient->m_SubArray[i].m_FloatArray[z] ) + return false; + } + + for( int iString=0; iString < sizeof(pServer->m_SubArray[0].m_Strings) / sizeof(pServer->m_SubArray[0].m_Strings[0]); iString++ ) + { + for( z=0; z < sizeof(pServer->m_SubArray[0].m_Strings[0]) / sizeof(pServer->m_SubArray[0].m_Strings[0][0]); z++ ) + { + if( pServer->m_SubArray[i].m_Strings[iString][z] != pClient->m_SubArray[i].m_Strings[iString][z] ) + return false; + + // Check for null termination. + if( pServer->m_SubArray[i].m_Strings[iString][z] == 0 ) + break; + } + } + } +#endif + + return true; +} + +void RandomlyChangeSubArray(DTTestServer *pServer) +{ +#if defined( SUPPORT_ARRAYS_OF_DATATABLES ) + if( !g_bSendSub ) + return; + + for( int i=0; i < 2; i++ ) + { + int index = rand() & 1; + + for( int z=0; z < sizeof(pServer->m_SubArray[0].m_FloatArray) / sizeof(pServer->m_SubArray[0].m_FloatArray[0]); z++ ) + pServer->m_SubArray[index].m_FloatArray[z] = rand() * 0.932f; + + for( int iString=0; iString < sizeof(pServer->m_SubArray[0].m_Strings) / sizeof(pServer->m_SubArray[0].m_Strings[0]); iString++ ) + { + int stringLen = sizeof(pServer->m_SubArray[0].m_Strings[0]) / sizeof(pServer->m_SubArray[0].m_Strings[0][0]); + for( z=0; z < stringLen; z++ ) + pServer->m_SubArray[index].m_Strings[iString][z] = (char)rand(); + + // null-terminate it + pServer->m_SubArray[index].m_Strings[iString][stringLen-1] = 0; + } + } +#endif +} + +bool CompareSub2( DTTestClient *pClient, DTTestServer *pServer ) +{ + return memcmp( &pClient->m_Sub2, &pServer->m_Sub2, sizeof( pClient->m_Sub2 ) ) == 0; +} + +void RandomlyChangeSub2( DTTestServer *pServer ) +{ + pServer->m_Sub2.m_Int = rand(); +} + +bool CompareSub2Sub( DTTestClient *pClient, DTTestServer *pServer ) +{ + return pClient->m_Sub2.m_Sub.m_Int2 == pServer->m_Sub2.m_Sub.m_Int2; +} + +void RandomlyChangeSub2Sub( DTTestServer *pServer ) +{ + pServer->m_Sub2.m_Sub.m_Int2 = rand(); +} + + +bool CompareVLA( DTTestClient *pClient, DTTestServer *pServer ) +{ + if ( pClient->m_VLALength != pServer->m_VLALength ) + return false; + + for ( int i=0; i < pClient->m_VLALength; i++ ) + { + if ( pClient->m_VLA[i] != pServer->m_VLA[i] ) + return false; + } + + return true; +} + +void RandomlyChangeVLA( DTTestServer *pServer ) +{ + pServer->m_VLALength = rand() % ARRAYSIZE( pServer->m_VLA ); + for ( int i=0; i < pServer->m_VLALength; i++ ) + pServer->m_VLA[i] = rand() * rand(); +} + + +bool CompareUtlVectorStruct( DTTestClient *pClient, DTTestServer *pServer ) +{ + CUtlVector<CTestStruct> &c = pClient->m_Sub.m_UtlVectorStruct; + CUtlVector<CTestStruct> &s = pServer->m_Sub.m_UtlVectorStruct; + + if ( c.Count() != s.Count() ) + return false; + + for ( int i=0; i < c.Count(); i++ ) + { + if ( c[i].a != s[i].a || c[i].b != s[i].b || c[i].f != s[i].f ) + return false; + } + return true; +} + + +void RandomlyChangeUtlVectorStruct( DTTestServer *pServer ) +{ + if ( !g_bSendSub ) + return; + + int nElements = rand() % MAX_STRUCTARRAY_ELEMENTS; + pServer->m_Sub.m_UtlVectorStruct.SetSize( nElements ); + for ( int i=0; i < nElements; i++ ) + { + pServer->m_Sub.m_UtlVectorStruct[i].a = rand(); + pServer->m_Sub.m_UtlVectorStruct[i].b = rand(); + pServer->m_Sub.m_UtlVectorStruct[i].f = rand(); + } +} + +bool CompareUtlVectorFloat( DTTestClient *pClient, DTTestServer *pServer ) +{ + CUtlVector<float> &c = pClient->m_Sub.m_UtlVectorFloat; + CUtlVector<float> &s = pServer->m_Sub.m_UtlVectorFloat; + + if ( c.Count() != s.Count() ) + return false; + + for ( int i=0; i < c.Count(); i++ ) + { + if ( c[i] != s[i] ) + return false; + } + return true; +} + +void RandomlyChangeUtlVectorChar( DTTestServer *pServer ) +{ + if ( !g_bSendSub ) + return; + + int nElements = rand() % MAX_CHARARRAY_ELEMENTS; + pServer->m_Sub.m_UtlVectorChar.SetSize( nElements ); + for ( int i=0; i < nElements; i++ ) + pServer->m_Sub.m_UtlVectorChar[i] = (char)rand(); +} + + +bool CompareUtlVectorChar( DTTestClient *pClient, DTTestServer *pServer ) +{ + CUtlVector<char> &c = pClient->m_Sub.m_UtlVectorChar; + CUtlVector<char> &s = pServer->m_Sub.m_UtlVectorChar; + + if ( c.Count() != s.Count() ) + return false; + + for ( int i=0; i < c.Count(); i++ ) + { + if ( c[i] != s[i] ) + return false; + } + return true; +} + +void RandomlyChangeUtlVectorFloat( DTTestServer *pServer ) +{ + if ( !g_bSendSub ) + return; + + int nElements = rand() % MAX_FLOATARRAY_ELEMENTS; + pServer->m_Sub.m_UtlVectorFloat.SetSize( nElements ); + for ( int i=0; i < nElements; i++ ) + pServer->m_Sub.m_UtlVectorFloat[i] = rand() / 0.93; +} + + +typedef struct +{ + CompareElementFn m_CompareFn; + RandomlyChangeElementFn m_ChangeFn; +} VarTestInfo; + +VarTestInfo g_VarTestInfos[] = +{ + {CompareVLA, RandomlyChangeVLA}, + {CompareUtlVectorStruct,RandomlyChangeUtlVectorStruct}, + {CompareUtlVectorFloat, RandomlyChangeUtlVectorFloat}, + {CompareUtlVectorChar, RandomlyChangeUtlVectorChar}, + {CompareFloat, RandomlyChangeFloat}, + {CompareSub2, RandomlyChangeSub2}, + {CompareSub2Sub, RandomlyChangeSub2Sub}, + {CompareInt, RandomlyChangeInt}, + {CompareFloatArray, RandomlyChangeFloatArray}, + {CompareTestSubString0, RandomlyChangeSubString0}, + {CompareTestSubString1, RandomlyChangeSubString1}, + {CompareCharArray, RandomlyChangeCharArray}, + {CompareVector, RandomlyChangeVector}, + {CompareString, RandomlyChangeString}, + {CompareIntArray, RandomlyChangeIntArray}, + {CompareSubArray, RandomlyChangeSubArray} +}; +#define NUMVARTESTINFOS (sizeof(g_VarTestInfos) / sizeof(g_VarTestInfos[0])) + + +int g_GuardOffsets[] = +{ + offsetof( DTTestClient, m_Guard1 ), + offsetof( DTTestClient, m_Guard2 ), + offsetof( DTTestClient, m_Guard3 ), + offsetof( DTTestClient, m_Guard4 ), + offsetof( DTTestClient, m_Guard5 ), + offsetof( DTTestClient, m_Guard6 ), + offsetof( DTTestClient, m_Guard7 ), + offsetof( DTTestClient, m_Guard8 ), + offsetof( DTTestClient, m_Guard9 ) +}; +int g_nGuardOffsets = sizeof( g_GuardOffsets ) / sizeof( g_GuardOffsets[0] ); + + +void SetGuardBytes( DTTestClient *pClient ) +{ + for( int i=0; i < g_nGuardOffsets; i++ ) + { + unsigned char *pDest = ((unsigned char *)pClient) + g_GuardOffsets[i]; + *((long*)pDest) = i; + } +} + + +void CheckGuardBytes( DTTestClient *pClient ) +{ + for( int i=0; i < g_nGuardOffsets; i++ ) + { + unsigned char *pDest = ((unsigned char *)pClient) + g_GuardOffsets[i]; + Assert( *((long*)pDest) == i ); + } +} + + +// ------------------------------------------------------------------------------------------- // +// TEST CODE +// ------------------------------------------------------------------------------------------- // + +bool CompareDTTest(DTTestClient *pClient, DTTestServer *pServer) +{ + for(int iVar=0; iVar < NUMVARTESTINFOS; iVar++) + { + if(!g_VarTestInfos[iVar].m_CompareFn(pClient, pServer)) + { + Assert( !"CompareDTTest: comparison failed. There is a new datatable bug." ); + return false; + } + } + return true; +} + +bool WriteSendTable_R( SendTable *pTable, bf_write &bfWrite, bool bNeedsDecoder ) +{ + if( pTable->GetWriteFlag() ) + return true; + + pTable->SetWriteFlag( true ); + + // Send the version with the exclude props. + bfWrite.WriteOneBit( 1 ); + + bfWrite.WriteOneBit( bNeedsDecoder?1:0 ); + + if( !SendTable_WriteInfos( pTable, &bfWrite ) ) + return false; + + for( int i=0; i < pTable->m_nProps; i++ ) + { + SendProp *pProp = &pTable->m_pProps[i]; + + if( pProp->m_Type == DPT_DataTable ) + if( !WriteSendTable_R( pProp->GetDataTable(), bfWrite, false ) ) + return false; + } + + return true; +} + + +void RunDataTableTest() +{ + RecvTable *pRecvTable = &REFERENCE_RECV_TABLE(DT_DTTest); + SendTable *pSendTable = &REFERENCE_SEND_TABLE(DT_DTTest); + + + ALIGN4 unsigned char buf[4096] ALIGN4_POST; + bf_write x = bf_write(buf, 4096); + bf_read y = bf_read(buf, 4096); + x.WriteUBitLong(1, 1); + x.WriteUBitLong(3, 2); + x.WriteUBitLong(7, 3); + x.WriteUBitLong(0x31415926, 32); + Verify( y.ReadOneBit() == 1 ); + Verify( y.ReadUBitLong(5) == 7*4+3 ); + Verify( y.ReadUBitLong(32) == 0x31415926 ); + + + // Initialize the send and receive modules. + SendTable_Init( &pSendTable, 1 ); + RecvTable_Init( &pRecvTable, 1 ); + + pSendTable->SetWriteFlag( false ); + + // Send DataTable info to the client. + ALIGN4 unsigned char commBuf[8192] ALIGN4_POST; + bf_write bfWrite( "RunDataTableTest->commBuf", commBuf, sizeof(commBuf) ); + if( !WriteSendTable_R( pSendTable, bfWrite, true ) ) + { + Assert( !"RunDataTableTest: SendTable_SendInfo failed." ); + } + bfWrite.WriteOneBit(0); + + + // Receive the SendTable's info. + bf_read bfRead( "RunDataTableTest->bfRead", commBuf, sizeof(commBuf)); + while( bfRead.ReadOneBit() ) + { + bool bNeedsDecoder = bfRead.ReadOneBit()!=0; + + if( !RecvTable_RecvClassInfos( &bfRead, bNeedsDecoder ) ) + { + Assert( !"RunDataTableTest: RecvTable_ReadInfos failed." ); + continue; + } + } + + // Register our receive table. + if( !RecvTable_CreateDecoders( NULL, false ) ) + { + Assert(false); + } + + + // Setup the data with all zeros. + DTTestServer dtServer; + DTTestClient dtClient; + + ALIGN4 unsigned char prevEncoded[4096] ALIGN4_POST; + ALIGN4 unsigned char fullEncoded[4096] ALIGN4_POST; + + memset(&dtServer, 0, sizeof(dtServer)); + memset(&dtClient, 0, sizeof(dtClient)); + memset(prevEncoded, 0, sizeof(prevEncoded)); + + SetGuardBytes( &dtClient ); + + // Now loop around, changing the data a little bit each time and send/recv deltas. + int nIterations = 25; + for( int iIteration=0; iIteration < nIterations; iIteration++ ) + { + // Change the server's data. + g_bSendSub = true; + if( (iIteration % 5) == 0 ) + { + g_bSendSub = false; // every 8th time, don't send the subtable + } + + if( (iIteration & 3) == 0 ) + { + // Every once in a while, change ALL the properties. + for( int iChange=0; iChange < NUMVARTESTINFOS; iChange++ ) + g_VarTestInfos[iChange].m_ChangeFn( &dtServer ); + } + else + { + int nChanges = 3 + rand() % NUMVARTESTINFOS; + for( int iChange=0; iChange < nChanges; iChange++ ) + { + int iInfo = rand() % NUMVARTESTINFOS; + g_VarTestInfos[iInfo].m_ChangeFn( &dtServer ); + } + } + + // Fully encode it. + bf_write bfFullEncoded( "RunDataTableTest->bfFullEncoded", fullEncoded, sizeof(fullEncoded) ); + if( !SendTable_Encode( pSendTable, &dtServer, &bfFullEncoded, -1, NULL ) ) + { + Assert(false); + } + + + ALIGN4 unsigned char deltaEncoded[4096] ALIGN4_POST; + bf_write bfDeltaEncoded( "RunDataTableTest->bfDeltaEncoded", deltaEncoded, sizeof(deltaEncoded) ); + + if ( iIteration == 0 ) + { + // On the first iteration, just write the whole state. + if( !SendTable_Encode( pSendTable, &dtServer, &bfDeltaEncoded, -1, NULL ) ) + { + Assert( false ); + } + } + else + { + // Figure out the delta between the newly encoded one and the previously encoded one. + ALIGN4 int deltaProps[MAX_DATATABLE_PROPS] ALIGN4_POST; + + bf_read fullEncodedRead( "RunDataTableTest->fullEncodedRead", fullEncoded, sizeof( fullEncoded ), bfFullEncoded.GetNumBitsWritten() ); + bf_read prevEncodedRead( "RunDataTableTest->prevEncodedRead", prevEncoded, sizeof( prevEncoded ) ); + + int nDeltaProps = SendTable_CalcDelta( + pSendTable, + prevEncoded, sizeof( prevEncoded ) * 8, + fullEncoded, bfFullEncoded.GetNumBitsWritten(), + deltaProps, + ARRAYSIZE( deltaProps ), + -1 ); + + Assert( nDeltaProps != -1 ); // BAD: buffer overflow + + + // Reencode with just the delta. This is what is actually sent to the client. + SendTable_WritePropList( + pSendTable, + fullEncoded, + bfFullEncoded.GetNumBitsWritten(), + &bfDeltaEncoded, + -1111, + deltaProps, + nDeltaProps ); + } + + memcpy( prevEncoded, fullEncoded, sizeof( prevEncoded ) ); + + + // This step isn't necessary to have the client decode the data but it's here to test + // RecvTable_CopyEncoding (and RecvTable_MergeDeltas). This call should just make an exact + // copy of the encoded data. + ALIGN4 unsigned char copyEncoded[4096] ALIGN4_POST; + bf_read bfReadDeltaEncoded( "RunDataTableTest->bfReadDeltaEncoded", deltaEncoded, sizeof( deltaEncoded ) ); + bf_write bfCopyEncoded( "RunDataTableTest->bfCopyEncoded", copyEncoded, sizeof(copyEncoded) ); + + RecvTable_CopyEncoding( pRecvTable, &bfReadDeltaEncoded, &bfCopyEncoded, -1 ); + + // Decode.. + bf_read bfDecode( "RunDataTableTest->copyEncoded", copyEncoded, sizeof( copyEncoded ) ); + if(!RecvTable_Decode(pRecvTable, &dtClient, &bfDecode, 1111)) + { + Assert(false); + } + + + // Make sure it didn't go into memory it shouldn't have. + CheckGuardBytes( &dtClient ); + + + // Verify that only the changed properties were sent and that they were received correctly. + CompareDTTest( &dtClient, &dtServer ); + } + + SendTable_Term(); + RecvTable_Term(); +} + + +#endif |