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