STATS.CPP 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. /*
  2. ** Command & Conquer Red Alert(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Command & Conquer *
  23. * *
  24. * File Name : WINSTUB.CPP *
  25. * *
  26. * Programmer : Steve Tall *
  27. * *
  28. * Start Date : 05/29/1996 *
  29. * *
  30. * Last Update : May 29th 1996 [ST] *
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Overview: *
  34. * Internet game statistics to collect and upload to the server *
  35. * *
  36. * *
  37. *---------------------------------------------------------------------------------------------*
  38. * *
  39. * Functions: *
  40. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  41. #ifdef WIN32
  42. #include "function.h"
  43. #include "tcpip.h"
  44. #include "packet.h"
  45. #include "ccdde.h"
  46. #define FIELD_PACKET_TYPE "TYPE"
  47. #define FIELD_GAME_ID "IDNO"
  48. #define FIELD_START_CREDITS "CRED"
  49. #define FIELD_BASES "BASE"
  50. #define FIELD_TIBERIUM "TIBR"
  51. #define FIELD_CRATES "CRAT"
  52. #define FIELD_AI_PLAYERS "AIPL"
  53. #define FIELD_CAPTURE_THE_FLAG "FLAG"
  54. #define FIELD_START_UNIT_COUNT "UNIT"
  55. #define FIELD_TECH_LEVEL "TECH"
  56. #define FIELD_SCENARIO "SCEN"
  57. #define FIELD_COMPLETION "CMPL"
  58. #define FIELD_START_TIME "TIME"
  59. #define FIELD_GAME_DURATION "DURA"
  60. #define FIELD_FRAME_RATE "AFPS"
  61. #define FIELD_SPEED_SETTING "SPED"
  62. #define FIELD_GAME_VERSION "VERS"
  63. #define FIELD_GAME_BUILD_DATE "DATE"
  64. #define FIELD_COVERT_PRESENT "COVT"
  65. #define FIELD_CPU_TYPE "PROC"
  66. #define FIELD_MEMORY "MEMO"
  67. #define FIELD_VIDEO_MEMORY "VIDM"
  68. #define FIELD_SHADOW_REGROWS "SHAD"
  69. #ifdef WOLAPI_INTEGRATION
  70. #define FIELD_HOSTORNOT "SDFX"
  71. #define FIELD_TOURNAMENT "TRNY"
  72. #define FIELD_NUM_INITIAL_PLAYERS "NUMP"
  73. #define FIELD_NUM_REMAINING_PLAYERS "REMN"
  74. #define FIELD_DISCONNECT_PINGS "PING"
  75. #define FIELD_COMPUTERTOOKOVER "QUIT"
  76. //#define FIELD_HARDWARE_GUID "GUID"
  77. #define FIELD_PLAYER1_IP "ADR1"
  78. #define FIELD_PLAYER2_IP "ADR2"
  79. #endif
  80. // ajw The following were never used (thank god).
  81. #define FIELD_PLAYER1_HANDLE "NAM1"
  82. #define FIELD_PLAYER2_HANDLE "NAM2"
  83. #define FIELD_PLAYER1_TEAM "SID1"
  84. #define FIELD_PLAYER2_TEAM "SID2"
  85. #define FIELD_PLAYER1_COLOR "COL1"
  86. #define FIELD_PLAYER2_COLOR "COL2"
  87. #define FIELD_PLAYER1_CREDITS "CRD1"
  88. #define FIELD_PLAYER2_CREDITS "CRD2"
  89. #define FIELD_PLAYER1_UNITS_LEFT "UNL1"
  90. #define FIELD_PLAYER2_UNITS_LEFT "UNL2"
  91. #define FIELD_PLAYER1_INFANTRY_LEFT "INL1"
  92. #define FIELD_PLAYER2_INFANTRY_LEFT "INL2"
  93. #define FIELD_PLAYER1_PLANES_LEFT "PLL1"
  94. #define FIELD_PLAYER2_PLANES_LEFT "PLL2"
  95. #define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1"
  96. #define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2"
  97. #define FIELD_PLAYER1_VESSELS_LEFT "VSL1"
  98. #define FIELD_PLAYER2_VESSELS_LEFT "VSL2"
  99. #define FIELD_PLAYER1_UNITS_BOUGHT "UNB1"
  100. #define FIELD_PLAYER2_UNITS_BOUGHT "UNB2"
  101. #define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1"
  102. #define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2"
  103. #define FIELD_PLAYER1_PLANES_BOUGHT "PLB1"
  104. #define FIELD_PLAYER2_PLANES_BOUGHT "PLB2"
  105. #define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1"
  106. #define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2"
  107. #define FIELD_PLAYER1_VESSELS_BOUGHT "VSB1"
  108. #define FIELD_PLAYER2_VESSELS_BOUGHT "VSB2"
  109. #define FIELD_PLAYER1_UNITS_KILLED "UNK1"
  110. #define FIELD_PLAYER2_UNITS_KILLED "UNK2"
  111. #define FIELD_PLAYER1_INFANTRY_KILLED "INK1"
  112. #define FIELD_PLAYER2_INFANTRY_KILLED "INK2"
  113. #define FIELD_PLAYER1_PLANES_KILLED "PLK1"
  114. #define FIELD_PLAYER2_PLANES_KILLED "PLK2"
  115. #define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1"
  116. #define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2"
  117. #define FIELD_PLAYER1_VESSELS_KILLED "VSK1"
  118. #define FIELD_PLAYER2_VESSELS_KILLED "VSK2"
  119. #define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1"
  120. #define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2"
  121. #define FIELD_PLAYER1_CRATES_FOUND "CRA1"
  122. #define FIELD_PLAYER2_CRATES_FOUND "CRA2"
  123. #define FIELD_PLAYER1_HARVESTED "HRV1"
  124. #define FIELD_PLAYER2_HARVESTED "HRV2"
  125. #define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50
  126. #define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51
  127. // Note: These enums match those in the game results server code.
  128. enum {
  129. COMPLETION_CONNECTION_LOST,
  130. COMPLETION_PLAYER_1_WON,
  131. COMPLETION_PLAYER_1_WON_BY_RESIGNATION,
  132. COMPLETION_PLAYER_1_WON_BY_DISCONNECTION,
  133. COMPLETION_PLAYER_2_WON,
  134. COMPLETION_PLAYER_2_WON_BY_RESIGNATION,
  135. COMPLETION_PLAYER_2_WON_BY_DISCONNECTION,
  136. #ifdef FIXIT_VERSION_3 // Stalemate games.
  137. COMPLETION_WASH = 64,
  138. #endif
  139. };
  140. extern unsigned long PlanetWestwoodGameID;
  141. extern HINSTANCE ProgramInstance;
  142. extern unsigned long PlanetWestwoodStartTime;
  143. extern "C" char CPUType;
  144. bool GameTimerInUse = false;
  145. TimerClass GameTimer;
  146. long GameEndTime;
  147. void *PacketLater = NULL;
  148. #include "WolDebug.h"
  149. #ifdef WOLAPI_INTEGRATION
  150. #include "WolapiOb.h"
  151. extern WolapiObject* pWolapi;
  152. extern bool bReconnectDialogCancelled;
  153. #endif
  154. /***********************************************************************************************
  155. * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server *
  156. * *
  157. * *
  158. * *
  159. * INPUT: Nothing *
  160. * *
  161. * OUTPUT: Nothing *
  162. * *
  163. * WARNINGS: None *
  164. * *
  165. * HISTORY: *
  166. * 5/29/96 12:38PM ST : Created *
  167. *=============================================================================================*/
  168. void Send_Statistics_Packet(void)
  169. {
  170. // debugprint( "Stats: Send_Statistics_Packet() called.\n" );
  171. #ifndef INTERNET_OFF // Denzil 5/4/98
  172. #ifdef WOLAPI_INTEGRATION
  173. if( !pWolapi ) // Should no longer ever happen.
  174. return;
  175. #endif
  176. PacketClass stats;
  177. HouseClass *player;
  178. static int packet_size;
  179. int index;
  180. bool packet_later = false; // Should the packet be sent later
  181. void *packet;
  182. static char field_player_handle[5] = { "NAM?" };
  183. static char field_player_team[5] = { "SID?" };
  184. static char field_player_color[5] = { "COL?" };
  185. static char field_player_credits[5] = { "CRD?" };
  186. static char field_player_units_left[5] = { "UNL?" };
  187. static char field_player_infantry_left[5] = { "INL?" };
  188. static char field_player_planes_left[5] = { "PLL?" };
  189. static char field_player_buildings_left[5] = { "BLL?" };
  190. static char field_player_vessels_left[5] = { "VSL?" };
  191. static char field_player_units_bought[5] = { "UNB?" };
  192. static char field_player_infantry_bought[5] = { "INB?" };
  193. static char field_player_planes_bought[5] = { "PLB?" };
  194. static char field_player_buildings_bought[5] = { "BLB?" };
  195. static char field_player_vessels_bought[5] = { "VSB?" };
  196. static char field_player_units_killed[5] = { "UNK?" };
  197. static char field_player_infantry_killed[5] = { "INK?" };
  198. static char field_player_planes_killed[5] = { "PLK?" };
  199. static char field_player_buildings_killed[5] = { "BLK?" };
  200. static char field_player_vessels_killed[5] = { "VSK?" };
  201. static char field_player_buildings_captured[5] = { "BLC?" };
  202. static char field_player_crates_found[5] = { "CRA?" };
  203. static char field_player_harvested[5] = { "HRV?" };
  204. static char *houses[] = {
  205. "SPA",
  206. "GRE",
  207. "USS",
  208. "ENG",
  209. "ITA",
  210. "GER",
  211. "FRA",
  212. "TKY",
  213. "GUD",
  214. "BAD",
  215. "CIV",
  216. "JP ",
  217. "M01",
  218. "M02",
  219. "M03",
  220. "M04",
  221. "M05",
  222. "M06",
  223. "M07",
  224. "M08"
  225. };
  226. if (!PacketLater){
  227. /*
  228. ** Field to identify this as C&C 95 internet game statistics packet
  229. */
  230. #ifdef WOLAPI_INTEGRATION
  231. if( pWolapi->bGameServer )
  232. {
  233. stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)0 ); // Reversed meaning of this for Neal.
  234. }else{
  235. stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)1 );
  236. }
  237. #else
  238. if (Server){
  239. stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO);
  240. }else{
  241. stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO);
  242. }
  243. #endif
  244. /*
  245. ** Game ID. A unique game identifier assigned by WChat.
  246. */
  247. stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID);
  248. #ifdef WOLAPI_INTEGRATION
  249. // Number of players initially in game.
  250. stats.Add_Field( FIELD_NUM_INITIAL_PLAYERS, (unsigned long)pWolapi->GameInfoCurrent.iPlayerCount );
  251. //debugprint( "Stats: number of initial players is %i\n", pWolapi->GameInfoCurrent.iPlayerCount );
  252. // Number of players remaining in game. Not sure of what use this will be statistically...
  253. stats.Add_Field( FIELD_NUM_REMAINING_PLAYERS, (unsigned long)Session.Players.Count() );
  254. //debugprint( "Stats: number of remaining players is %i\n", Session.Players.Count() );
  255. // Whether or not this was a tournament game.
  256. stats.Add_Field( FIELD_TOURNAMENT, (unsigned char)( pWolapi->GameInfoCurrent.bTournament ? 1 : 0 ) );
  257. // ajw This is now in WOLAPI...
  258. // // A unique value that identifies the machine that the game was played on.
  259. // HW_PROFILE_INFO hwinfo;
  260. // ::GetCurrentHwProfile( &hwinfo );
  261. // stats.Add_Field( FIELD_HARDWARE_GUID, hwinfo.szHwProfileGuid );
  262. #endif
  263. /*
  264. ** Start credits.
  265. */
  266. stats.Add_Field(FIELD_START_CREDITS, (unsigned long)Session.Options.Credits);
  267. /*
  268. ** Bases (On/Off)
  269. */
  270. stats.Add_Field(FIELD_BASES, Session.Options.Bases ? "ON" : "OFF");
  271. /*
  272. ** Tiberium (On/Off)
  273. */
  274. stats.Add_Field(FIELD_TIBERIUM, Session.Options.Tiberium ? "ON" : "OFF");
  275. /*
  276. ** Crates (On/Off)
  277. */
  278. stats.Add_Field(FIELD_CRATES, Session.Options.Goodies ? "ON" : "OFF");
  279. /*
  280. ** AI Players (On/Off)
  281. */
  282. stats.Add_Field(FIELD_AI_PLAYERS, (unsigned long) Session.Options.AIPlayers);
  283. /*
  284. ** Shadow regrowth enabled
  285. */
  286. stats.Add_Field(FIELD_SHADOW_REGROWS, Special.IsShadowGrow ? "ON" : "OFF" );
  287. /*
  288. ** Capture the flag mode (On/Off)
  289. */
  290. stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF");
  291. /*
  292. ** Start unit count
  293. */
  294. stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)Session.Options.UnitCount);
  295. /*
  296. ** Tech level.
  297. */
  298. stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel);
  299. /*
  300. ** Scenario
  301. */
  302. #if (1)
  303. stats.Add_Field(FIELD_SCENARIO, Session.Options.ScenarioDescription);
  304. #else //(1)
  305. char fname[128];
  306. char namebuffer[40];
  307. char *abuffer = (char *)_ShapeBuffer;
  308. memset(abuffer, '\0', _ShapeBufferSize);
  309. sprintf(fname,"%s.INI",Scen.ScenarioName);
  310. CCFileClass fileo;
  311. fileo.Set_Name (fname);
  312. fileo.Read(abuffer, _ShapeBufferSize-1);
  313. fileo.Close();
  314. WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer);
  315. stats.Add_Field(FIELD_SCENARIO, namebuffer);
  316. //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]);
  317. #endif //(1)
  318. #ifdef WOLAPI_INTEGRATION
  319. // Completion status is set for Tournament games only - ajw.
  320. if( pWolapi->GameInfoCurrent.bTournament )
  321. {
  322. #endif
  323. /*
  324. ** Game completion status.
  325. **
  326. ** Connection lost.
  327. ** Player 1 won
  328. ** Player 2 won
  329. ** Player 1 won by resignation
  330. ** Player 2 won by resignation
  331. ** Player 1 aborted
  332. ** Player 2 aborted
  333. **
  334. ** Game was a draw
  335. */
  336. HouseClass *player1 = NULL;
  337. HouseClass *player2 = NULL;
  338. for (int h=0 ; h<Session.Players.Count(); h++){
  339. HouseClass *ptr = HouseClass::As_Pointer ((HousesType)(h + HOUSE_MULTI1));
  340. if (ptr->IsHuman){
  341. if (player1){
  342. player2 = ptr;
  343. break;
  344. }else{
  345. player1 = ptr;
  346. }
  347. }
  348. }
  349. int completion = -1;
  350. if (player1 && player2){ // Can this ever fail? ajw
  351. #ifdef FIXIT_VERSION_3
  352. // Send IP addresses of both players.
  353. NetNumType net;
  354. NetNodeType node;
  355. char szIPAddress[ 30 ];
  356. Session.Players[ 0 ]->Address.Get_Address( net, node );
  357. sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] );
  358. if( strcmp( szIPAddress, "255.255.255.255" ) == 0 )
  359. {
  360. // Ok. It's not set. Let's try to get it ourselves...
  361. char szHostName[ 512 ];
  362. int iRes = gethostname( szHostName, 512 );
  363. if( iRes != SOCKET_ERROR ) // else forget about trying
  364. {
  365. // debugprint( "gethostname got me %s\n", szHostName );
  366. struct hostent* pHostent = gethostbyname( szHostName );
  367. if( pHostent ) // else forget about trying
  368. {
  369. int i = 0;
  370. int* piAddress = (int*)pHostent->h_addr_list[ i ];
  371. while( piAddress )
  372. {
  373. // There is a non-null value for this h_addr_list entry.
  374. char szAsciiIP[ 30 ];
  375. strcpy( szAsciiIP, inet_ntoa( *( (struct in_addr*)piAddress ) ) );
  376. // We have an address in the right form.
  377. // Now, is it an address in a private network? If so we should ignore it.
  378. unsigned char q1 = ( (char*)piAddress )[ 0 ]; // First digit.
  379. unsigned char q2 = ( (char*)piAddress )[ 1 ]; // Second digit.
  380. // debugprint( "ip: %s\n", szAsciiIP );
  381. if( q1 == 10 || ( q1 == 172 && ( q2 >= 16 && q2 <= 31 ) ) || ( q1 == 192 && q2 == 168 ) )
  382. {
  383. // This is a private network address - ignore it and go on to next.
  384. }
  385. else
  386. {
  387. strcpy( szIPAddress, szAsciiIP );
  388. break;
  389. }
  390. piAddress = (int*)pHostent->h_addr_list[ ++i ];
  391. }
  392. }
  393. // else
  394. // debugprint( "gethostbyname failed. Error %i\n", WSAGetLastError() );
  395. }
  396. // else
  397. // debugprint( "gethostname failed with %i, error %i\n", iRes, WSAGetLastError() );
  398. }
  399. stats.Add_Field( FIELD_PLAYER1_IP, (char*)szIPAddress );
  400. Session.Players[ 1 ]->Address.Get_Address( net, node );
  401. sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] );
  402. stats.Add_Field( FIELD_PLAYER2_IP, (char*)szIPAddress );
  403. #endif
  404. #ifdef FIXIT_VERSION_3 // Stalemate games.
  405. if( Scen.bLocalProposesDraw && Scen.bOtherProposesDraw )
  406. {
  407. completion = COMPLETION_WASH;
  408. }
  409. else
  410. {
  411. #endif
  412. if (ConnectionLost){
  413. #ifdef WOLAPI_INTEGRATION
  414. if( bReconnectDialogCancelled )
  415. {
  416. if( Session.Players[ 0 ]->Player.ID == HOUSE_MULTI1 )
  417. // I am player1.
  418. completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
  419. else
  420. completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
  421. }
  422. else
  423. {
  424. completion = COMPLETION_CONNECTION_LOST;
  425. if( pWolapi->bDisconnectPingingCompleted )
  426. {
  427. char szPingResult[ 8 ]; // Format is "x/y a/b", e.g., "3/5 4/5"
  428. pWolapi->DisconnectPingResultsString( szPingResult );
  429. stats.Add_Field( FIELD_DISCONNECT_PINGS, (char*)szPingResult );
  430. }
  431. // else
  432. // debugprint( "Stats: bDisconnectPingingCompleted is false! Should be finished!!!!!!!!!!!!!!!\n" );
  433. }
  434. #else
  435. completion = COMPLETION_CONNECTION_LOST;
  436. #endif
  437. }else{
  438. if (player1->IsGiverUpper){
  439. completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
  440. }
  441. if (player2->IsGiverUpper){
  442. completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
  443. }
  444. if (player2->IsDefeated){
  445. /*
  446. ** Player 1 won. Find out how.
  447. */
  448. completion = COMPLETION_PLAYER_1_WON;
  449. if (player2->IsResigner){
  450. completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION;
  451. }else{
  452. if (player2->IsGiverUpper){
  453. completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
  454. }
  455. }
  456. }else{
  457. if (player1->IsDefeated){
  458. /*
  459. ** Player 2 won. Find out how.
  460. */
  461. completion = COMPLETION_PLAYER_2_WON;
  462. if (player1->IsResigner){
  463. completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION;
  464. }else{
  465. if (player1->IsGiverUpper){
  466. completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
  467. }
  468. }
  469. }
  470. }
  471. }
  472. #ifdef FIXIT_VERSION_3 // Stalemate games.
  473. }
  474. #endif
  475. }
  476. stats.Add_Field (FIELD_COMPLETION, (char) completion);
  477. //debugprint( "Stats: Tournament game completion value: %i\n", completion );
  478. #ifdef WOLAPI_INTEGRATION
  479. }
  480. #endif
  481. /*
  482. ** Game start time (GMT or Pacific?)
  483. **
  484. ** Passed from WChat
  485. */
  486. stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime);
  487. /*
  488. ** Game duration (seconds).
  489. */
  490. stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60);
  491. /*
  492. ** Avg. frame rate.
  493. */
  494. #ifdef FIXIT_IP_CRASH
  495. long divisor = GameEndTime / 60;
  496. if (divisor != 0) {
  497. stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) );
  498. } else {
  499. stats.Add_Field (FIELD_FRAME_RATE, 0l);
  500. }
  501. #else
  502. stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) );
  503. #endif
  504. /*
  505. ** CPU type
  506. */
  507. stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType);
  508. /*
  509. ** Memory
  510. */
  511. MEMORYSTATUS mem_info;
  512. mem_info.dwLength=sizeof(mem_info);
  513. GlobalMemoryStatus(&mem_info);
  514. stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys);
  515. /*
  516. ** Video memory
  517. */
  518. DDCAPS video_capabilities;
  519. long video_memory;
  520. if (DirectDrawObject){
  521. video_capabilities.dwSize = sizeof (video_capabilities);
  522. if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){
  523. video_memory = video_capabilities.dwVidMemTotal;
  524. video_memory += 1024*1024 -1;
  525. video_memory &= 0xfff00000;
  526. stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory);
  527. }
  528. }
  529. /*
  530. ** Game speed setting.
  531. */
  532. stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed);
  533. /*
  534. ** Red Alert version/build date
  535. */
  536. char version[128];
  537. sprintf (version, "V%s", VerNum.Version_Name() );
  538. stats.Add_Field (FIELD_GAME_VERSION, (char*)version);
  539. char path_to_exe[280];
  540. FILETIME write_time; //File time is 64 bits
  541. GetModuleFileName (ProgramInstance, path_to_exe, 280);
  542. RawFileClass file;
  543. file.Set_Name(path_to_exe);
  544. file.Open();
  545. HANDLE handle = file.Get_File_Handle();
  546. if (handle != INVALID_HANDLE_VALUE){
  547. if (GetFileTime (handle, NULL, NULL, &write_time)){
  548. write_time.dwLowDateTime = htonl (write_time.dwLowDateTime);
  549. write_time.dwHighDateTime = htonl (write_time.dwHighDateTime);
  550. stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time));
  551. }
  552. }
  553. /*
  554. ** Covert installed? (Yes/No)
  555. */
  556. //stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present());
  557. /*
  558. ** Build the player specific statistics
  559. **
  560. */
  561. #ifdef WOLAPI_INTEGRATION
  562. for (int house = 0 ; house < 8 ; house++){
  563. #else
  564. for (int house = 0 ; house < 2 ; house++){
  565. #endif
  566. player = HouseClass::As_Pointer((HousesType) (house + HOUSE_MULTI1));
  567. #ifdef WOLAPI_INTEGRATION
  568. if( !player )
  569. continue;
  570. #endif
  571. /*
  572. ** Player handle.
  573. */
  574. field_player_handle[3] = '1' + (char)house;
  575. #ifdef WOLAPI_INTEGRATION
  576. stats.Add_Field (field_player_handle, (char*) player->InitialName);
  577. //debugprint( "Stats: Player %i name %s\n", house, (char*) player->InitialName );
  578. //debugprint( "Stats: Player %i ending name %s\n", house, (char*) player->IniName );
  579. #else
  580. stats.Add_Field (field_player_handle, (char*) player->IniName);
  581. #endif
  582. #ifdef WOLAPI_INTEGRATION
  583. // Whether or not this player was taken over by the computer, due to his quitting the game.
  584. if( strcmp( player->IniName, player->InitialName ) )
  585. stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)1 );
  586. else
  587. stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)0 );
  588. #endif
  589. /*
  590. ** Player team. (NOD or GDI)
  591. */
  592. field_player_team[3] = '1' + (char)house;
  593. stats.Add_Field (field_player_team, houses[player->ActLike]);
  594. /*
  595. ** Player color
  596. */
  597. field_player_color[3] = '1' + (char)house;
  598. stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1));
  599. /*
  600. ** Player end credits.
  601. */
  602. field_player_credits[3] = '1' + (char)house;
  603. stats.Add_Field (field_player_credits, player->Credits + player->Tiberium);
  604. /*
  605. ** Number of each unit/building type built
  606. */
  607. field_player_infantry_bought[3] = '1' + (char)house;
  608. field_player_units_bought[3] = '1' + (char)house;
  609. field_player_planes_bought[3] = '1' + (char)house;
  610. field_player_buildings_bought[3] = '1' + (char)house;
  611. field_player_vessels_bought[3] = '1' + (char)house;
  612. player->InfantryTotals->To_Network_Format();
  613. player->UnitTotals->To_Network_Format();
  614. player->AircraftTotals->To_Network_Format();
  615. player->BuildingTotals->To_Network_Format();
  616. player->VesselTotals->To_Network_Format();
  617. stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
  618. stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
  619. stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
  620. stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
  621. stats.Add_Field (field_player_vessels_bought, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4);
  622. player->InfantryTotals->To_PC_Format();
  623. player->UnitTotals->To_PC_Format();
  624. player->AircraftTotals->To_PC_Format();
  625. player->BuildingTotals->To_PC_Format();
  626. /*
  627. ** Clear out the counts and use the space to count up the current number of units/buildings
  628. */
  629. player->InfantryTotals->Clear_Unit_Total();
  630. player->AircraftTotals->Clear_Unit_Total();
  631. player->UnitTotals->Clear_Unit_Total();
  632. player->BuildingTotals->Clear_Unit_Total();
  633. player->VesselTotals->Clear_Unit_Total();
  634. /*
  635. ** Number of units remaining to player
  636. */
  637. for (index = 0; index < Units.Count(); index++) {
  638. UnitClass const * unit = Units.Ptr(index);
  639. if (player == unit->House){
  640. player->UnitTotals->Increment_Unit_Total (unit->Class->Type);
  641. }
  642. }
  643. for (index = 0; index < Infantry.Count(); index++) {
  644. InfantryClass const * infantry = Infantry.Ptr(index);
  645. if (player == infantry->House && !infantry->Class->IsCivilian){
  646. player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type);
  647. }
  648. }
  649. for (index = 0; index < Aircraft.Count(); index++) {
  650. AircraftClass const * aircraft = Aircraft.Ptr(index);
  651. if (player == aircraft->House){ // && aircraft->Class->Type != AIRCRAFT_CARGO){
  652. player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type);
  653. }
  654. }
  655. for (index = 0; index < Buildings.Count(); index++) {
  656. BuildingClass const * building = Buildings.Ptr(index);
  657. if (player == building->House){
  658. player->BuildingTotals->Increment_Unit_Total (building->Class->Type);
  659. }
  660. }
  661. for (index = 0; index < Vessels.Count(); index++) {
  662. VesselClass const * vessel = Vessels.Ptr(index);
  663. if (player == vessel->House){
  664. player->VesselTotals->Increment_Unit_Total (vessel->Class->Type);
  665. }
  666. }
  667. player->InfantryTotals->To_Network_Format();
  668. player->UnitTotals->To_Network_Format();
  669. player->AircraftTotals->To_Network_Format();
  670. player->BuildingTotals->To_Network_Format();
  671. player->VesselTotals->To_Network_Format();
  672. field_player_infantry_left[3] = '1' + (char)house;
  673. field_player_units_left[3] = '1' + (char)house;
  674. field_player_planes_left[3] = '1' + (char)house;
  675. field_player_buildings_left[3] = '1' + (char)house;
  676. #ifdef FIXIT_IP_STATS
  677. field_player_vessels_left[3] = '1' + (char)house;
  678. #endif
  679. stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
  680. stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
  681. stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
  682. stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
  683. stats.Add_Field (field_player_vessels_left, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4);
  684. /*
  685. ** Number of enemy units/buildings of each type destroyed.
  686. */
  687. player->DestroyedInfantry->To_Network_Format();
  688. player->DestroyedUnits->To_Network_Format();
  689. player->DestroyedAircraft->To_Network_Format();
  690. player->DestroyedBuildings->To_Network_Format();
  691. player->DestroyedVessels->To_Network_Format();
  692. field_player_infantry_killed[3] = '1' + (char)house;
  693. field_player_units_killed[3] = '1' + (char)house;
  694. field_player_planes_killed[3] = '1' + (char)house;
  695. field_player_buildings_killed[3] = '1' + (char)house;
  696. field_player_vessels_killed[3] = '1' + (char)house;
  697. stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4);
  698. stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4);
  699. stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4);
  700. stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4);
  701. #ifdef FIXIT_VERSION_3
  702. stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedVessels->Get_Unit_Count()*4);
  703. #else
  704. stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4);
  705. #endif
  706. /*
  707. ** Number and type of enemy buildings captured
  708. */
  709. field_player_buildings_captured[3] = '1' + (char)house;
  710. player->CapturedBuildings->To_Network_Format();
  711. stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4);
  712. /*
  713. ** Number of crates discovered and their contents
  714. */
  715. field_player_crates_found[3] = '1' + (char)house;
  716. player->TotalCrates->To_Network_Format();
  717. stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4);
  718. /*
  719. ** Amount of tiberium turned into credits
  720. */
  721. field_player_harvested[3] = '1' + (char)house;
  722. stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits);
  723. }
  724. /*
  725. ** Create the comms packet to be sent
  726. */
  727. packet = stats.Create_Comms_Packet(packet_size);
  728. #ifndef WOLAPI_INTEGRATION // ajw - 'PacketLater' is no longer ever used.
  729. /*
  730. ** If a player disconnected then dont send the packet at this time - save it for later
  731. */
  732. if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION
  733. || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){
  734. PacketLater = packet;
  735. return;
  736. }
  737. #endif
  738. }else{ //else for if (!PacketLater)
  739. /*
  740. ** Send the packet we calculated earlier when the disconnect occurred
  741. */
  742. packet = PacketLater;
  743. PacketLater = NULL;
  744. }
  745. /*
  746. ** Send it.....
  747. */
  748. const char* szGameResServer;
  749. int iPort;
  750. if( pWolapi->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME )
  751. {
  752. szGameResServer = pWolapi->szGameResServerHost2;
  753. iPort = pWolapi->iGameResServerPort2;
  754. }
  755. else
  756. {
  757. szGameResServer = pWolapi->szGameResServerHost1;
  758. iPort = pWolapi->iGameResServerPort1;
  759. }
  760. if( *szGameResServer )
  761. {
  762. if( pWolapi->pNetUtil->RequestGameresSend( szGameResServer, iPort, (unsigned char*)packet, packet_size ) != S_OK )
  763. //debugprint( "RequestGameresSend( %s, %i ) failed!!!\n", szGameResServer, iPort );
  764. ;
  765. }
  766. /*
  767. ** Save it to disk as well so I can see it
  768. */
  769. // RawFileClass anotherfile ("packet.net");
  770. // anotherfile.Write(packet, packet_size);
  771. //debugprint( "Wrote out packet.net\n" );
  772. /*
  773. ** Tidy up
  774. */
  775. delete [] packet;
  776. GameStatisticsPacketSent = true;
  777. #endif // INTERNET_OFF
  778. }
  779. void Register_Game_Start_Time(void)
  780. {
  781. GameTimer.Set (0, true);
  782. GameTimerInUse = true;
  783. }
  784. extern void Register_Game_End_Time(void)
  785. {
  786. GameEndTime = GameTimer.Time();
  787. GameTimerInUse = false;
  788. }
  789. #endif //WIN32