summaryrefslogtreecommitdiff
path: root/engine/dt_test.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/dt_test.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/dt_test.cpp')
-rw-r--r--engine/dt_test.cpp1010
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