Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1010 lines
28 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // ---------------------------------------------------------------------------------------- //
  9. // This is a datatable test case.
  10. // It is run in debug mode when the engine starts to catch any bugs in datatable code.
  11. // This can also serve as a simple example of how datatables work separately from the
  12. // intricacies of the entity system.
  13. // ---------------------------------------------------------------------------------------- //
  14. // This is also a good place to test new code since it's run right when the engine
  15. // starts up. It can also be put into standalone apps easily.
  16. // ---------------------------------------------------------------------------------------- //
  17. // Things it tests:
  18. // - Data transmission integrity.
  19. // - Delta calculation.
  20. // - Strings, floats, vectors, recursive datatables, and ints.
  21. // - Fixed-length arrays.
  22. // - Variable-length arrays.
  23. // - Exclude props.
  24. // - Recursive datatables.
  25. // - Datatable proxies returning false.
  26. // - CUtlVectors of regular types (like floats) and data tables.
  27. // ---------------------------------------------------------------------------------------- //
  28. // Things it does not test:
  29. // - Quantization.
  30. // - Clamping.
  31. // - The entity system's usage of data tables.
  32. // - Stress testing - too many properties, maxing out delta bits.
  33. // - Built-in and custom client and server proxies.
  34. // ---------------------------------------------------------------------------------------- //
  35. // At a high level, the test is setup as such:
  36. // - Server structure and datatable.
  37. // - Client structure and datatable.
  38. // - A function table with a function to modify and compare each element.
  39. // - A function that initializes the server structure and tries random changes to it and
  40. // verifies that the client receives the deltas and changes correctly.
  41. // ---------------------------------------------------------------------------------------- //
  42. // Eventually it would be nice to stress-test the entities with tests for:
  43. // - Misordered proxy callbacks and missing data.
  44. // ---------------------------------------------------------------------------------------- //
  45. #include "quakedef.h"
  46. #include "dt.h"
  47. #include "dt_send.h"
  48. #include "dt_recv.h"
  49. #include "tier0/dbg.h"
  50. #include "dt_utlvector_send.h"
  51. #include "dt_utlvector_recv.h"
  52. // memdbgon must be the last include file in a .cpp file!!!
  53. #include "tier0/memdbgon.h"
  54. // If datatables support these again, then uncomment this to have them tested.
  55. //#define SUPPORT_ARRAYS_OF_DATATABLES
  56. #ifdef _DEBUG
  57. class DTTestSub2Sub
  58. {
  59. public:
  60. int m_Int2;
  61. };
  62. class DTTestSub2
  63. {
  64. public:
  65. int m_Int;
  66. DTTestSub2Sub m_Sub;
  67. };
  68. class CTestStruct
  69. {
  70. public:
  71. int a,b;
  72. float f;
  73. };
  74. #define MAX_STRUCTARRAY_ELEMENTS 11
  75. #define MAX_FLOATARRAY_ELEMENTS 18
  76. #define MAX_CHARARRAY_ELEMENTS 22
  77. // ------------------------------------------------------------------------------------------- //
  78. // DTTestServerSub and its DataTable.
  79. // ------------------------------------------------------------------------------------------- //
  80. class DTTestServerSub
  81. {
  82. public:
  83. float m_FloatArray[3];
  84. char m_Strings[2][64];
  85. CUtlVector<CTestStruct> m_UtlVectorStruct;
  86. CUtlVector<float> m_UtlVectorFloat;
  87. CUtlVector<char> m_UtlVectorChar;
  88. };
  89. void SendProxy_DTTestServerSubString( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
  90. {
  91. SendProxy_StringToString( pProp, pStruct, pData, pOut, iElement, objectID);
  92. }
  93. BEGIN_SEND_TABLE_NOBASE( CTestStruct, DT_TestStruct )
  94. SendPropInt( SENDINFO_NOCHECK( a ) ),
  95. SendPropInt( SENDINFO_NOCHECK( b ) ),
  96. SendPropFloat( SENDINFO_NOCHECK( f ) )
  97. END_SEND_TABLE()
  98. BEGIN_SEND_TABLE_NOBASE(DTTestServerSub, DT_DTTestSub)
  99. // - Auto type conversions (receiving an array of floats into an array of ints).
  100. SendPropArray(
  101. SendPropFloat(SENDINFO_NOCHECK(m_FloatArray[0]), 0, SPROP_NOSCALE),
  102. m_FloatArray),
  103. SendPropUtlVectorDataTable( m_UtlVectorStruct, MAX_STRUCTARRAY_ELEMENTS, DT_TestStruct ),
  104. SendPropArray(
  105. SendPropString(SENDINFO_NOCHECK(m_Strings[0]), 0, SendProxy_DTTestServerSubString),
  106. m_Strings ),
  107. SendPropUtlVector(
  108. SENDINFO_UTLVECTOR( m_UtlVectorChar ),
  109. MAX_CHARARRAY_ELEMENTS,
  110. SendPropInt( NULL, 0, sizeof( char ), 0 ) ),
  111. SendPropUtlVector(
  112. SENDINFO_UTLVECTOR( m_UtlVectorFloat ),
  113. MAX_FLOATARRAY_ELEMENTS, // max elements
  114. SendPropFloat( NULL, 0, 0, 0, SPROP_NOSCALE ) )
  115. END_SEND_TABLE()
  116. BEGIN_SEND_TABLE_NOBASE(DTTestSub2Sub, DT_DTTestSub2Sub)
  117. SendPropInt( SENDINFO_NOCHECK( m_Int2 ), 32 ),
  118. END_SEND_TABLE()
  119. BEGIN_SEND_TABLE_NOBASE(DTTestSub2, DT_DTTestSub2)
  120. SendPropDataTable(SENDINFO_DT(m_Sub), &REFERENCE_SEND_TABLE(DT_DTTestSub2Sub)),
  121. SendPropInt( SENDINFO_NOCHECK( m_Int ), 32 ),
  122. END_SEND_TABLE()
  123. // ------------------------------------------------------------------------------------------- //
  124. // DTTestServer and its DataTable.
  125. // ------------------------------------------------------------------------------------------- //
  126. class DTTestServer
  127. {
  128. public:
  129. DTTestServerSub m_Sub;
  130. DTTestSub2 m_Sub2;
  131. float m_Float;
  132. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  133. DTTestServerSub m_SubArray[2];
  134. #endif
  135. Vector m_Vector;
  136. char m_String[64];
  137. int m_Int;
  138. int m_IntArray[32]; // Note that the server and client array length are different.
  139. char m_CharArray[8];
  140. int m_VLALength;
  141. int m_VLA[16];
  142. };
  143. void SendProxy_DTTestServerFloat( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID )
  144. {
  145. SendProxy_FloatToFloat(pProp, pStruct, pData, pOut, iElement, objectID);
  146. }
  147. void SendProxy_DTTestServerVector( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID )
  148. {
  149. SendProxy_VectorToVector(pProp, pStruct, pData, pOut, iElement, objectID);
  150. }
  151. void SendProxy_DTTestServerString( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID )
  152. {
  153. SendProxy_StringToString(pProp, pStruct, pData, pOut, iElement, objectID);
  154. }
  155. void SendProxy_DTTestServerInt( const SendProp *pProp, void *pStruct, void *pData, DVariant *pOut, int iElement, int objectID )
  156. {
  157. SendProxy_Int32ToInt32(pProp, pStruct, pData, pOut, iElement, objectID);
  158. }
  159. bool g_bSendSub = true;
  160. void* SendProxy_DTTestServerSub( const SendProp *pProp, const void *pStruct, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  161. {
  162. if( !g_bSendSub )
  163. return NULL;
  164. return SendProxy_DataTableToDataTable( pProp, pStruct, pData, pRecipients, objectID );
  165. }
  166. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_DTTestServerSub );
  167. int ArrayLengthSendProxy_VLALength( const void *pStruct, int objectID )
  168. {
  169. DTTestServer *pServer = (DTTestServer*)pStruct;
  170. return pServer->m_VLALength;
  171. }
  172. BEGIN_SEND_TABLE_NOBASE(DTTestServer, DT_DTTest)
  173. SendPropVariableLengthArray(
  174. ArrayLengthSendProxy_VLALength,
  175. SendPropInt( SENDINFO_NOCHECK( m_VLA[0] ) ),
  176. m_VLA ),
  177. // Test exclude props.
  178. SendPropExclude( "DT_DTTest", "m_Int" ),
  179. SendPropDataTable(SENDINFO_DT(m_Sub), &REFERENCE_SEND_TABLE(DT_DTTestSub), SendProxy_DTTestServerSub),
  180. SendPropFloat (SENDINFO_NOCHECK(m_Float), 32, SPROP_NOSCALE),
  181. SendPropDataTable(SENDINFO_DT(m_Sub2), &REFERENCE_SEND_TABLE(DT_DTTestSub2)),
  182. SendPropInt (SENDINFO_NOCHECK(m_Int), 23, SPROP_UNSIGNED),
  183. SendPropExclude( "DT_DTTestSub", "m_FloatArray" ),
  184. SendPropString(SENDINFO_NOCHECK(m_String)),
  185. SendPropArray(
  186. SendPropInt(SENDINFO_NOCHECK(m_CharArray[0]), 8),
  187. m_CharArray),
  188. SendPropArray(
  189. SendPropInt (SENDINFO_NOCHECK(m_IntArray[0]), 23, SPROP_UNSIGNED),
  190. m_IntArray),
  191. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  192. SendPropArray(
  193. SendPropDataTable(SENDINFO_DT(m_SubArray[0]), &REFERENCE_SEND_TABLE(DT_DTTestSub), SendProxy_DTTestServerSub),
  194. m_SubArray ),
  195. #endif
  196. SendPropVector(SENDINFO_NOCHECK(m_Vector), 32, SPROP_NOSCALE)
  197. END_SEND_TABLE()
  198. // ------------------------------------------------------------------------------------------- //
  199. // DTTestClientSub and its DataTable.
  200. // ------------------------------------------------------------------------------------------- //
  201. class DTTestClientSub
  202. {
  203. public:
  204. char m_Strings[2][64];
  205. float m_FloatArray[3];
  206. CUtlVector<CTestStruct> m_UtlVectorStruct;
  207. CUtlVector<float> m_UtlVectorFloat;
  208. CUtlVector<char> m_UtlVectorChar;
  209. };
  210. void RecvProxy_DTTestClientSubString( const CRecvProxyData *pData, void *pStruct, void *pOut )
  211. {
  212. RecvProxy_StringToString( pData, pStruct, pOut );
  213. }
  214. BEGIN_RECV_TABLE_NOBASE( CTestStruct, DT_TestStruct )
  215. RecvPropInt( RECVINFO( a ) ),
  216. RecvPropInt( RECVINFO( b ) ),
  217. RecvPropFloat( RECVINFO( f ) ),
  218. END_RECV_TABLE()
  219. BEGIN_RECV_TABLE_NOBASE(DTTestClientSub, DT_DTTestSub)
  220. // - Auto type conversions (receiving an array of floats into an array of ints).
  221. RecvPropArray(
  222. RecvPropFloat(RECVINFO(m_FloatArray[0])),
  223. m_FloatArray),
  224. RecvPropUtlVector( RECVINFO_UTLVECTOR( m_UtlVectorFloat ), MAX_FLOATARRAY_ELEMENTS, RecvPropFloat(NULL,0,0) ),
  225. RecvPropUtlVectorDataTable( m_UtlVectorStruct, MAX_STRUCTARRAY_ELEMENTS, DT_TestStruct ),
  226. RecvPropUtlVector(
  227. RECVINFO_UTLVECTOR( m_UtlVectorChar ),
  228. MAX_CHARARRAY_ELEMENTS,
  229. RecvPropInt( NULL, 0, sizeof( char ) ) ),
  230. RecvPropArray(
  231. RecvPropString(RECVINFO(m_Strings[0]), 0, RecvProxy_DTTestClientSubString),
  232. m_Strings),
  233. END_RECV_TABLE()
  234. BEGIN_RECV_TABLE_NOBASE(DTTestSub2Sub, DT_DTTestSub2Sub)
  235. RecvPropInt( RECVINFO( m_Int2 ), 32 ),
  236. END_RECV_TABLE()
  237. BEGIN_RECV_TABLE_NOBASE(DTTestSub2, DT_DTTestSub2)
  238. RecvPropDataTable(RECVINFO_DT(m_Sub), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub2Sub)),
  239. RecvPropInt( RECVINFO( m_Int ) ),
  240. END_RECV_TABLE()
  241. // ------------------------------------------------------------------------------------------- //
  242. // DTTestClient and DataTable.
  243. // ------------------------------------------------------------------------------------------- //
  244. class DTTestClient
  245. {
  246. public:
  247. DTTestClientSub m_Sub;
  248. long m_Guard1;
  249. DTTestSub2 m_Sub2;
  250. long m_Guard2;
  251. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  252. DTTestClientSub m_SubArray[2];
  253. #endif
  254. long m_Guard3;
  255. float m_Float;
  256. long m_Guard4;
  257. Vector m_Vector;
  258. long m_Guard5;
  259. char m_String[64];
  260. long m_Guard6;
  261. int m_Int;
  262. long m_Guard7;
  263. int m_IntArray[32]; // Note that the server and client array length are different.
  264. long m_Guard8;
  265. char m_CharArray[8];
  266. long m_Guard9;
  267. int m_VLALength;
  268. int m_VLA[16];
  269. };
  270. void RecvProxyArrayLength_VLA( void *pStruct, int objectID, int currentArrayLength )
  271. {
  272. DTTestClient *pClient = (DTTestClient*)pStruct;
  273. pClient->m_VLALength = currentArrayLength;
  274. }
  275. BEGIN_RECV_TABLE_NOBASE(DTTestClient, DT_DTTest)
  276. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  277. RecvPropArray(
  278. RecvPropDataTable(RECVINFO_DT(m_SubArray[0]), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub), RecvProxy_DTTestClientSub),
  279. m_SubArray ),
  280. #endif
  281. RecvPropFloat (RECVINFO(m_Float), 0),
  282. RecvPropDataTable(RECVINFO_DT(m_Sub), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub)),
  283. RecvPropDataTable(RECVINFO_DT(m_Sub2), 0, &REFERENCE_RECV_TABLE(DT_DTTestSub2)),
  284. // - Arrays with and without the SPROP_ONEBITDELTA flag.
  285. RecvPropArray(
  286. RecvPropInt (RECVINFO(m_CharArray[0]), 8),
  287. m_CharArray),
  288. RecvPropVector(RECVINFO(m_Vector), 0),
  289. RecvPropString(RECVINFO_STRING(m_String), 0),
  290. RecvPropInt (RECVINFO(m_Int), 0),
  291. // - Arrays with and without the SPROP_ONEBITDELTA flag.
  292. // - Array size mismatches between the client and the server.
  293. RecvPropArray(
  294. RecvPropInt (RECVINFO(m_IntArray[0]), 0),
  295. m_IntArray),
  296. RecvPropInt( RECVINFO( m_VLALength ) ),
  297. RecvPropVariableLengthArray(
  298. RecvProxyArrayLength_VLA,
  299. RecvPropInt( RECVINFO( m_VLA[0] ) ),
  300. m_VLA )
  301. END_RECV_TABLE()
  302. // ------------------------------------------------------------------------------------------- //
  303. // Functions that act on the data.
  304. // ------------------------------------------------------------------------------------------- //
  305. typedef bool (*CompareElementFn)(DTTestClient *pClient, DTTestServer *pServer);
  306. typedef void (*RandomlyChangeElementFn)(DTTestServer *pServer);
  307. float FRand(double minVal, double maxVal)
  308. {
  309. return (float)(((double)rand() / VALVE_RAND_MAX) * (maxVal - minVal) + minVal);
  310. }
  311. void RandomlyChangeStringGeneric(char *str, int size)
  312. {
  313. for(int i=0; i < size-1; i++)
  314. str[i] = (char)rand();
  315. str[size-1] = 0;
  316. }
  317. bool CompareTestSubString0(DTTestClient *pClient, DTTestServer *pServer)
  318. {
  319. return strcmp(pClient->m_Sub.m_Strings[0], pServer->m_Sub.m_Strings[0]) == 0;
  320. }
  321. void RandomlyChangeSubString0(DTTestServer *pServer)
  322. {
  323. if( g_bSendSub )
  324. RandomlyChangeStringGeneric(pServer->m_Sub.m_Strings[0], sizeof(pServer->m_Sub.m_Strings[0]));
  325. }
  326. bool CompareTestSubString1(DTTestClient *pClient, DTTestServer *pServer)
  327. {
  328. return strcmp(pClient->m_Sub.m_Strings[1], pServer->m_Sub.m_Strings[1]) == 0;
  329. }
  330. void RandomlyChangeSubString1(DTTestServer *pServer)
  331. {
  332. if( g_bSendSub )
  333. RandomlyChangeStringGeneric(pServer->m_Sub.m_Strings[1], sizeof(pServer->m_Sub.m_Strings[1]));
  334. }
  335. bool CompareFloat(DTTestClient *pClient, DTTestServer *pServer)
  336. {
  337. return pClient->m_Float == pServer->m_Float;
  338. }
  339. void RandomlyChangeFloat(DTTestServer *pServer)
  340. {
  341. pServer->m_Float = FRand(-500000, 500000);
  342. }
  343. bool CompareVector(DTTestClient *pClient, DTTestServer *pServer)
  344. {
  345. return pClient->m_Vector.x == pServer->m_Vector.x &&
  346. pClient->m_Vector.y == pServer->m_Vector.y &&
  347. pClient->m_Vector.z == pServer->m_Vector.z;
  348. }
  349. void RandomlyChangeVector(DTTestServer *pServer)
  350. {
  351. pServer->m_Vector.x = FRand(-500000, 500000);
  352. pServer->m_Vector.y = FRand(-500000, 500000);
  353. pServer->m_Vector.z = FRand(-500000, 500000);
  354. }
  355. bool CompareString(DTTestClient *pClient, DTTestServer *pServer)
  356. {
  357. return strcmp(pClient->m_String, pServer->m_String) == 0;
  358. }
  359. void RandomlyChangeString(DTTestServer *pServer)
  360. {
  361. //memset( pServer->m_String, , sizeof( pServer->m_String ) );
  362. Q_strncpy( pServer->m_String, "a", sizeof( pServer->m_String ) );
  363. //RandomlyChangeStringGeneric(pServer->m_String, sizeof(pServer->m_String));
  364. }
  365. bool CompareInt(DTTestClient *pClient, DTTestServer *pServer)
  366. {
  367. // (m_Int is the exclude prop we're testing)
  368. // return pClient->m_Int == pServer->m_Int;
  369. return true;
  370. }
  371. void RandomlyChangeInt(DTTestServer *pServer)
  372. {
  373. pServer->m_Int = (int)rand();
  374. }
  375. bool CompareIntArray(DTTestClient *pClient, DTTestServer *pServer)
  376. {
  377. // Just verify however much of the data we can.
  378. 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]));
  379. return memcmp(pClient->m_IntArray, pServer->m_IntArray, leastElements*sizeof(int)) == 0;
  380. }
  381. void RandomlyChangeIntArray(DTTestServer *pServer)
  382. {
  383. // Change a random subset of the array.
  384. int nElements = sizeof(pServer->m_IntArray) / sizeof(pServer->m_IntArray[0]);
  385. int nChanges = 4 + rand() % nElements;
  386. for(int i=0; i < nChanges; i++)
  387. {
  388. pServer->m_IntArray[rand() % nElements] = (int)rand();
  389. }
  390. }
  391. bool CompareFloatArray(DTTestClient *pClient, DTTestServer *pServer)
  392. {
  393. // m_FloatArray is an ExcludeProp.
  394. /*
  395. 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]));
  396. for(int i=0; i < leastElements; i++)
  397. {
  398. if(pClient->m_Sub.m_FloatArray[i] != pServer->m_Sub.m_FloatArray[i])
  399. return false;
  400. }
  401. */
  402. return true;
  403. }
  404. void RandomlyChangeFloatArray(DTTestServer *pServer)
  405. {
  406. // Change a random subset of the array.
  407. int nElements = sizeof(pServer->m_Sub.m_FloatArray) / sizeof(pServer->m_Sub.m_FloatArray[0]);
  408. int nChanges = 4 + rand() % nElements;
  409. for(int i=0; i < nChanges; i++)
  410. {
  411. pServer->m_Sub.m_FloatArray[rand() % nElements] = (float)rand() * 0.943123f;
  412. }
  413. }
  414. bool CompareCharArray(DTTestClient *pClient, DTTestServer *pServer)
  415. {
  416. return memcmp(pClient->m_CharArray, pServer->m_CharArray, sizeof(pClient->m_CharArray)) == 0;
  417. }
  418. void RandomlyChangeCharArray(DTTestServer *pServer)
  419. {
  420. for(int i=0; i < (sizeof(pServer->m_CharArray) / sizeof(pServer->m_CharArray[0])); i++)
  421. pServer->m_CharArray[i] = (char)rand();
  422. }
  423. bool CompareSubArray(DTTestClient *pClient, DTTestServer *pServer )
  424. {
  425. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  426. for( int i=0; i < 2; i++ )
  427. {
  428. for( int z=0; z < sizeof(pServer->m_SubArray[0].m_FloatArray) / sizeof(pServer->m_SubArray[0].m_FloatArray[0]); z++ )
  429. {
  430. if( pServer->m_SubArray[i].m_FloatArray[z] != pClient->m_SubArray[i].m_FloatArray[z] )
  431. return false;
  432. }
  433. for( int iString=0; iString < sizeof(pServer->m_SubArray[0].m_Strings) / sizeof(pServer->m_SubArray[0].m_Strings[0]); iString++ )
  434. {
  435. for( z=0; z < sizeof(pServer->m_SubArray[0].m_Strings[0]) / sizeof(pServer->m_SubArray[0].m_Strings[0][0]); z++ )
  436. {
  437. if( pServer->m_SubArray[i].m_Strings[iString][z] != pClient->m_SubArray[i].m_Strings[iString][z] )
  438. return false;
  439. // Check for null termination.
  440. if( pServer->m_SubArray[i].m_Strings[iString][z] == 0 )
  441. break;
  442. }
  443. }
  444. }
  445. #endif
  446. return true;
  447. }
  448. void RandomlyChangeSubArray(DTTestServer *pServer)
  449. {
  450. #if defined( SUPPORT_ARRAYS_OF_DATATABLES )
  451. if( !g_bSendSub )
  452. return;
  453. for( int i=0; i < 2; i++ )
  454. {
  455. int index = rand() & 1;
  456. for( int z=0; z < sizeof(pServer->m_SubArray[0].m_FloatArray) / sizeof(pServer->m_SubArray[0].m_FloatArray[0]); z++ )
  457. pServer->m_SubArray[index].m_FloatArray[z] = rand() * 0.932f;
  458. for( int iString=0; iString < sizeof(pServer->m_SubArray[0].m_Strings) / sizeof(pServer->m_SubArray[0].m_Strings[0]); iString++ )
  459. {
  460. int stringLen = sizeof(pServer->m_SubArray[0].m_Strings[0]) / sizeof(pServer->m_SubArray[0].m_Strings[0][0]);
  461. for( z=0; z < stringLen; z++ )
  462. pServer->m_SubArray[index].m_Strings[iString][z] = (char)rand();
  463. // null-terminate it
  464. pServer->m_SubArray[index].m_Strings[iString][stringLen-1] = 0;
  465. }
  466. }
  467. #endif
  468. }
  469. bool CompareSub2( DTTestClient *pClient, DTTestServer *pServer )
  470. {
  471. return memcmp( &pClient->m_Sub2, &pServer->m_Sub2, sizeof( pClient->m_Sub2 ) ) == 0;
  472. }
  473. void RandomlyChangeSub2( DTTestServer *pServer )
  474. {
  475. pServer->m_Sub2.m_Int = rand();
  476. }
  477. bool CompareSub2Sub( DTTestClient *pClient, DTTestServer *pServer )
  478. {
  479. return pClient->m_Sub2.m_Sub.m_Int2 == pServer->m_Sub2.m_Sub.m_Int2;
  480. }
  481. void RandomlyChangeSub2Sub( DTTestServer *pServer )
  482. {
  483. pServer->m_Sub2.m_Sub.m_Int2 = rand();
  484. }
  485. bool CompareVLA( DTTestClient *pClient, DTTestServer *pServer )
  486. {
  487. if ( pClient->m_VLALength != pServer->m_VLALength )
  488. return false;
  489. for ( int i=0; i < pClient->m_VLALength; i++ )
  490. {
  491. if ( pClient->m_VLA[i] != pServer->m_VLA[i] )
  492. return false;
  493. }
  494. return true;
  495. }
  496. void RandomlyChangeVLA( DTTestServer *pServer )
  497. {
  498. pServer->m_VLALength = rand() % ARRAYSIZE( pServer->m_VLA );
  499. for ( int i=0; i < pServer->m_VLALength; i++ )
  500. pServer->m_VLA[i] = rand() * rand();
  501. }
  502. bool CompareUtlVectorStruct( DTTestClient *pClient, DTTestServer *pServer )
  503. {
  504. CUtlVector<CTestStruct> &c = pClient->m_Sub.m_UtlVectorStruct;
  505. CUtlVector<CTestStruct> &s = pServer->m_Sub.m_UtlVectorStruct;
  506. if ( c.Count() != s.Count() )
  507. return false;
  508. for ( int i=0; i < c.Count(); i++ )
  509. {
  510. if ( c[i].a != s[i].a || c[i].b != s[i].b || c[i].f != s[i].f )
  511. return false;
  512. }
  513. return true;
  514. }
  515. void RandomlyChangeUtlVectorStruct( DTTestServer *pServer )
  516. {
  517. if ( !g_bSendSub )
  518. return;
  519. int nElements = rand() % MAX_STRUCTARRAY_ELEMENTS;
  520. pServer->m_Sub.m_UtlVectorStruct.SetSize( nElements );
  521. for ( int i=0; i < nElements; i++ )
  522. {
  523. pServer->m_Sub.m_UtlVectorStruct[i].a = rand();
  524. pServer->m_Sub.m_UtlVectorStruct[i].b = rand();
  525. pServer->m_Sub.m_UtlVectorStruct[i].f = rand();
  526. }
  527. }
  528. bool CompareUtlVectorFloat( DTTestClient *pClient, DTTestServer *pServer )
  529. {
  530. CUtlVector<float> &c = pClient->m_Sub.m_UtlVectorFloat;
  531. CUtlVector<float> &s = pServer->m_Sub.m_UtlVectorFloat;
  532. if ( c.Count() != s.Count() )
  533. return false;
  534. for ( int i=0; i < c.Count(); i++ )
  535. {
  536. if ( c[i] != s[i] )
  537. return false;
  538. }
  539. return true;
  540. }
  541. void RandomlyChangeUtlVectorChar( DTTestServer *pServer )
  542. {
  543. if ( !g_bSendSub )
  544. return;
  545. int nElements = rand() % MAX_CHARARRAY_ELEMENTS;
  546. pServer->m_Sub.m_UtlVectorChar.SetSize( nElements );
  547. for ( int i=0; i < nElements; i++ )
  548. pServer->m_Sub.m_UtlVectorChar[i] = (char)rand();
  549. }
  550. bool CompareUtlVectorChar( DTTestClient *pClient, DTTestServer *pServer )
  551. {
  552. CUtlVector<char> &c = pClient->m_Sub.m_UtlVectorChar;
  553. CUtlVector<char> &s = pServer->m_Sub.m_UtlVectorChar;
  554. if ( c.Count() != s.Count() )
  555. return false;
  556. for ( int i=0; i < c.Count(); i++ )
  557. {
  558. if ( c[i] != s[i] )
  559. return false;
  560. }
  561. return true;
  562. }
  563. void RandomlyChangeUtlVectorFloat( DTTestServer *pServer )
  564. {
  565. if ( !g_bSendSub )
  566. return;
  567. int nElements = rand() % MAX_FLOATARRAY_ELEMENTS;
  568. pServer->m_Sub.m_UtlVectorFloat.SetSize( nElements );
  569. for ( int i=0; i < nElements; i++ )
  570. pServer->m_Sub.m_UtlVectorFloat[i] = rand() / 0.93;
  571. }
  572. typedef struct
  573. {
  574. CompareElementFn m_CompareFn;
  575. RandomlyChangeElementFn m_ChangeFn;
  576. } VarTestInfo;
  577. VarTestInfo g_VarTestInfos[] =
  578. {
  579. {CompareVLA, RandomlyChangeVLA},
  580. {CompareUtlVectorStruct,RandomlyChangeUtlVectorStruct},
  581. {CompareUtlVectorFloat, RandomlyChangeUtlVectorFloat},
  582. {CompareUtlVectorChar, RandomlyChangeUtlVectorChar},
  583. {CompareFloat, RandomlyChangeFloat},
  584. {CompareSub2, RandomlyChangeSub2},
  585. {CompareSub2Sub, RandomlyChangeSub2Sub},
  586. {CompareInt, RandomlyChangeInt},
  587. {CompareFloatArray, RandomlyChangeFloatArray},
  588. {CompareTestSubString0, RandomlyChangeSubString0},
  589. {CompareTestSubString1, RandomlyChangeSubString1},
  590. {CompareCharArray, RandomlyChangeCharArray},
  591. {CompareVector, RandomlyChangeVector},
  592. {CompareString, RandomlyChangeString},
  593. {CompareIntArray, RandomlyChangeIntArray},
  594. {CompareSubArray, RandomlyChangeSubArray}
  595. };
  596. #define NUMVARTESTINFOS (sizeof(g_VarTestInfos) / sizeof(g_VarTestInfos[0]))
  597. int g_GuardOffsets[] =
  598. {
  599. offsetof( DTTestClient, m_Guard1 ),
  600. offsetof( DTTestClient, m_Guard2 ),
  601. offsetof( DTTestClient, m_Guard3 ),
  602. offsetof( DTTestClient, m_Guard4 ),
  603. offsetof( DTTestClient, m_Guard5 ),
  604. offsetof( DTTestClient, m_Guard6 ),
  605. offsetof( DTTestClient, m_Guard7 ),
  606. offsetof( DTTestClient, m_Guard8 ),
  607. offsetof( DTTestClient, m_Guard9 )
  608. };
  609. int g_nGuardOffsets = sizeof( g_GuardOffsets ) / sizeof( g_GuardOffsets[0] );
  610. void SetGuardBytes( DTTestClient *pClient )
  611. {
  612. for( int i=0; i < g_nGuardOffsets; i++ )
  613. {
  614. unsigned char *pDest = ((unsigned char *)pClient) + g_GuardOffsets[i];
  615. *((long*)pDest) = i;
  616. }
  617. }
  618. void CheckGuardBytes( DTTestClient *pClient )
  619. {
  620. for( int i=0; i < g_nGuardOffsets; i++ )
  621. {
  622. unsigned char *pDest = ((unsigned char *)pClient) + g_GuardOffsets[i];
  623. Assert( *((long*)pDest) == i );
  624. }
  625. }
  626. // ------------------------------------------------------------------------------------------- //
  627. // TEST CODE
  628. // ------------------------------------------------------------------------------------------- //
  629. bool CompareDTTest(DTTestClient *pClient, DTTestServer *pServer)
  630. {
  631. for(int iVar=0; iVar < NUMVARTESTINFOS; iVar++)
  632. {
  633. if(!g_VarTestInfos[iVar].m_CompareFn(pClient, pServer))
  634. {
  635. Assert( !"CompareDTTest: comparison failed. There is a new datatable bug." );
  636. return false;
  637. }
  638. }
  639. return true;
  640. }
  641. bool WriteSendTable_R( SendTable *pTable, bf_write &bfWrite, bool bNeedsDecoder )
  642. {
  643. if( pTable->GetWriteFlag() )
  644. return true;
  645. pTable->SetWriteFlag( true );
  646. // Send the version with the exclude props.
  647. bfWrite.WriteOneBit( 1 );
  648. bfWrite.WriteOneBit( bNeedsDecoder?1:0 );
  649. if( !SendTable_WriteInfos( pTable, &bfWrite ) )
  650. return false;
  651. for( int i=0; i < pTable->m_nProps; i++ )
  652. {
  653. SendProp *pProp = &pTable->m_pProps[i];
  654. if( pProp->m_Type == DPT_DataTable )
  655. if( !WriteSendTable_R( pProp->GetDataTable(), bfWrite, false ) )
  656. return false;
  657. }
  658. return true;
  659. }
  660. void RunDataTableTest()
  661. {
  662. RecvTable *pRecvTable = &REFERENCE_RECV_TABLE(DT_DTTest);
  663. SendTable *pSendTable = &REFERENCE_SEND_TABLE(DT_DTTest);
  664. ALIGN4 unsigned char buf[4096] ALIGN4_POST;
  665. bf_write x = bf_write(buf, 4096);
  666. bf_read y = bf_read(buf, 4096);
  667. x.WriteUBitLong(1, 1);
  668. x.WriteUBitLong(3, 2);
  669. x.WriteUBitLong(7, 3);
  670. x.WriteUBitLong(0x31415926, 32);
  671. Verify( y.ReadOneBit() == 1 );
  672. Verify( y.ReadUBitLong(5) == 7*4+3 );
  673. Verify( y.ReadUBitLong(32) == 0x31415926 );
  674. // Initialize the send and receive modules.
  675. SendTable_Init( &pSendTable, 1 );
  676. RecvTable_Init( &pRecvTable, 1 );
  677. pSendTable->SetWriteFlag( false );
  678. // Send DataTable info to the client.
  679. ALIGN4 unsigned char commBuf[8192] ALIGN4_POST;
  680. bf_write bfWrite( "RunDataTableTest->commBuf", commBuf, sizeof(commBuf) );
  681. if( !WriteSendTable_R( pSendTable, bfWrite, true ) )
  682. {
  683. Assert( !"RunDataTableTest: SendTable_SendInfo failed." );
  684. }
  685. bfWrite.WriteOneBit(0);
  686. // Receive the SendTable's info.
  687. bf_read bfRead( "RunDataTableTest->bfRead", commBuf, sizeof(commBuf));
  688. while( bfRead.ReadOneBit() )
  689. {
  690. bool bNeedsDecoder = bfRead.ReadOneBit()!=0;
  691. if( !RecvTable_RecvClassInfos( &bfRead, bNeedsDecoder ) )
  692. {
  693. Assert( !"RunDataTableTest: RecvTable_ReadInfos failed." );
  694. continue;
  695. }
  696. }
  697. // Register our receive table.
  698. if( !RecvTable_CreateDecoders( NULL, false ) )
  699. {
  700. Assert(false);
  701. }
  702. // Setup the data with all zeros.
  703. DTTestServer dtServer;
  704. DTTestClient dtClient;
  705. ALIGN4 unsigned char prevEncoded[4096] ALIGN4_POST;
  706. ALIGN4 unsigned char fullEncoded[4096] ALIGN4_POST;
  707. memset(&dtServer, 0, sizeof(dtServer));
  708. memset(&dtClient, 0, sizeof(dtClient));
  709. memset(prevEncoded, 0, sizeof(prevEncoded));
  710. SetGuardBytes( &dtClient );
  711. // Now loop around, changing the data a little bit each time and send/recv deltas.
  712. int nIterations = 25;
  713. for( int iIteration=0; iIteration < nIterations; iIteration++ )
  714. {
  715. // Change the server's data.
  716. g_bSendSub = true;
  717. if( (iIteration % 5) == 0 )
  718. {
  719. g_bSendSub = false; // every 8th time, don't send the subtable
  720. }
  721. if( (iIteration & 3) == 0 )
  722. {
  723. // Every once in a while, change ALL the properties.
  724. for( int iChange=0; iChange < NUMVARTESTINFOS; iChange++ )
  725. g_VarTestInfos[iChange].m_ChangeFn( &dtServer );
  726. }
  727. else
  728. {
  729. int nChanges = 3 + rand() % NUMVARTESTINFOS;
  730. for( int iChange=0; iChange < nChanges; iChange++ )
  731. {
  732. int iInfo = rand() % NUMVARTESTINFOS;
  733. g_VarTestInfos[iInfo].m_ChangeFn( &dtServer );
  734. }
  735. }
  736. // Fully encode it.
  737. bf_write bfFullEncoded( "RunDataTableTest->bfFullEncoded", fullEncoded, sizeof(fullEncoded) );
  738. if( !SendTable_Encode( pSendTable, &dtServer, &bfFullEncoded, -1, NULL ) )
  739. {
  740. Assert(false);
  741. }
  742. ALIGN4 unsigned char deltaEncoded[4096] ALIGN4_POST;
  743. bf_write bfDeltaEncoded( "RunDataTableTest->bfDeltaEncoded", deltaEncoded, sizeof(deltaEncoded) );
  744. if ( iIteration == 0 )
  745. {
  746. // On the first iteration, just write the whole state.
  747. if( !SendTable_Encode( pSendTable, &dtServer, &bfDeltaEncoded, -1, NULL ) )
  748. {
  749. Assert( false );
  750. }
  751. }
  752. else
  753. {
  754. // Figure out the delta between the newly encoded one and the previously encoded one.
  755. ALIGN4 int deltaProps[MAX_DATATABLE_PROPS] ALIGN4_POST;
  756. bf_read fullEncodedRead( "RunDataTableTest->fullEncodedRead", fullEncoded, sizeof( fullEncoded ), bfFullEncoded.GetNumBitsWritten() );
  757. bf_read prevEncodedRead( "RunDataTableTest->prevEncodedRead", prevEncoded, sizeof( prevEncoded ) );
  758. int nDeltaProps = SendTable_CalcDelta(
  759. pSendTable,
  760. prevEncoded, sizeof( prevEncoded ) * 8,
  761. fullEncoded, bfFullEncoded.GetNumBitsWritten(),
  762. deltaProps,
  763. ARRAYSIZE( deltaProps ),
  764. -1 );
  765. Assert( nDeltaProps != -1 ); // BAD: buffer overflow
  766. // Reencode with just the delta. This is what is actually sent to the client.
  767. SendTable_WritePropList(
  768. pSendTable,
  769. fullEncoded,
  770. bfFullEncoded.GetNumBitsWritten(),
  771. &bfDeltaEncoded,
  772. -1111,
  773. deltaProps,
  774. nDeltaProps );
  775. }
  776. memcpy( prevEncoded, fullEncoded, sizeof( prevEncoded ) );
  777. // This step isn't necessary to have the client decode the data but it's here to test
  778. // RecvTable_CopyEncoding (and RecvTable_MergeDeltas). This call should just make an exact
  779. // copy of the encoded data.
  780. ALIGN4 unsigned char copyEncoded[4096] ALIGN4_POST;
  781. bf_read bfReadDeltaEncoded( "RunDataTableTest->bfReadDeltaEncoded", deltaEncoded, sizeof( deltaEncoded ) );
  782. bf_write bfCopyEncoded( "RunDataTableTest->bfCopyEncoded", copyEncoded, sizeof(copyEncoded) );
  783. RecvTable_CopyEncoding( pRecvTable, &bfReadDeltaEncoded, &bfCopyEncoded, -1 );
  784. // Decode..
  785. bf_read bfDecode( "RunDataTableTest->copyEncoded", copyEncoded, sizeof( copyEncoded ) );
  786. if(!RecvTable_Decode(pRecvTable, &dtClient, &bfDecode, 1111))
  787. {
  788. Assert(false);
  789. }
  790. // Make sure it didn't go into memory it shouldn't have.
  791. CheckGuardBytes( &dtClient );
  792. // Verify that only the changed properties were sent and that they were received correctly.
  793. CompareDTTest( &dtClient, &dtServer );
  794. }
  795. SendTable_Term();
  796. RecvTable_Term();
  797. }
  798. #endif