CnC_Remastered_Collection/TIBERIANDAWN/STATS.CPP
PG-SteveT 03416d24e1 Initial Source Code commit
Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered.
2020-05-27 12:16:20 -07:00

677 lines
23 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/***********************************************************************************************
*** 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 ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : WINSTUB.CPP *
* *
* Programmer : Steve Tall *
* *
* Start Date : 05/29/1996 *
* *
* Last Update : May 29th 1996 [ST] *
* *
*---------------------------------------------------------------------------------------------*
* Overview: *
* Internet game statistics to collect and upload to the server *
* *
* *
*---------------------------------------------------------------------------------------------*
* *
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "tcpip.h"
#include "packet.h"
#include "ccdde.h"
#define FIELD_PACKET_TYPE "TYPE"
#define FIELD_GAME_ID "IDNO"
#define FIELD_START_CREDITS "CRED"
#define FIELD_BASES "BASE"
#define FIELD_TIBERIUM "TIBR"
#define FIELD_CRATES "CRAT"
#define FIELD_AI_PLAYERS "AIPL"
#define FIELD_CAPTURE_THE_FLAG "FLAG"
#define FIELD_START_UNIT_COUNT "UNIT"
#define FIELD_TECH_LEVEL "TECH"
#define FIELD_SCENARIO "SCEN"
#define FIELD_COMPLETION "CMPL"
#define FIELD_START_TIME "TIME"
#define FIELD_GAME_DURATION "DURA"
#define FIELD_FRAME_RATE "AFPS"
#define FIELD_SPEED_SETTING "SPED"
#define FIELD_GAME_VERSION "VERS"
#define FIELD_GAME_BUILD_DATE "DATE"
#define FIELD_COVERT_PRESENT "COVT"
#define FIELD_CPU_TYPE "PROC"
#define FIELD_MEMORY "MEMO"
#define FIELD_VIDEO_MEMORY "VIDM"
#define FIELD_PLAYER1_HANDLE "NAM1"
#define FIELD_PLAYER2_HANDLE "NAM2"
#define FIELD_PLAYER1_TEAM "SID1"
#define FIELD_PLAYER2_TEAM "SID2"
#define FIELD_PLAYER1_COLOR "COL1"
#define FIELD_PLAYER2_COLOR "COL2"
#define FIELD_PLAYER1_CREDITS "CRD1"
#define FIELD_PLAYER2_CREDITS "CRD2"
#define FIELD_PLAYER1_UNITS_LEFT "UNL1"
#define FIELD_PLAYER2_UNITS_LEFT "UNL2"
#define FIELD_PLAYER1_INFANTRY_LEFT "INL1"
#define FIELD_PLAYER2_INFANTRY_LEFT "INL2"
#define FIELD_PLAYER1_PLANES_LEFT "PLL1"
#define FIELD_PLAYER2_PLANES_LEFT "PLL2"
#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1"
#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2"
#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1"
#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2"
#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1"
#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2"
#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1"
#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2"
#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1"
#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2"
#define FIELD_PLAYER1_UNITS_KILLED "UNK1"
#define FIELD_PLAYER2_UNITS_KILLED "UNK2"
#define FIELD_PLAYER1_INFANTRY_KILLED "INK1"
#define FIELD_PLAYER2_INFANTRY_KILLED "INK2"
#define FIELD_PLAYER1_PLANES_KILLED "PLK1"
#define FIELD_PLAYER2_PLANES_KILLED "PLK2"
#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1"
#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2"
#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1"
#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2"
#define FIELD_PLAYER1_CRATES_FOUND "CRA1"
#define FIELD_PLAYER2_CRATES_FOUND "CRA2"
#define FIELD_PLAYER1_HARVESTED "HRV1"
#define FIELD_PLAYER2_HARVESTED "HRV2"
#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50
#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51
enum {
COMPLETION_CONNECTION_LOST,
COMPLETION_PLAYER_1_WON,
COMPLETION_PLAYER_1_WON_BY_RESIGNATION,
COMPLETION_PLAYER_1_WON_BY_DISCONNECTION,
COMPLETION_PLAYER_2_WON,
COMPLETION_PLAYER_2_WON_BY_RESIGNATION,
COMPLETION_PLAYER_2_WON_BY_DISCONNECTION
};
extern unsigned long PlanetWestwoodGameID;
extern HINSTANCE ProgramInstance;
extern unsigned long PlanetWestwoodStartTime;
extern "C" char CPUType;
bool GameTimerInUse = false;
TimerClass GameTimer;
long GameEndTime;
void *PacketLater = NULL;
/***********************************************************************************************
* Send_Statistics_To_Server -- sends internet game statistics to the Westeood server *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 5/29/96 12:38PM ST : Created *
*=============================================================================================*/
void Send_Statistics_Packet(void)
{
//PG_TO_FIX
#if (0)
#ifndef DEMO
PacketClass stats;
HouseClass *player;
static int packet_size;
int index;
bool packet_later = false; // Should the packet be sent later
void *packet;
static char field_player_handle[5] = { "NAM?" };
static char field_player_team[5] = { "SID?" };
static char field_player_color[5] = { "COL?" };
static char field_player_credits[5] = { "CRD?" };
static char field_player_units_left[5] = { "UNL?" };
static char field_player_infantry_left[5] = { "INL?" };
static char field_player_planes_left[5] = { "PLL?" };
static char field_player_buildings_left[5] = { "BLL?" };
static char field_player_units_bought[5] = { "UNB?" };
static char field_player_infantry_bought[5] = { "INB?" };
static char field_player_planes_bought[5] = { "PLB?" };
static char field_player_buildings_bought[5] = { "BLB?" };
static char field_player_units_killed[5] = { "UNK?" };
static char field_player_infantry_killed[5] = { "INK?" };
static char field_player_planes_killed[5] = { "PLK?" };
static char field_player_buildings_killed[5] = { "BLK?" };
static char field_player_buildings_captured[5] = { "BLC?" };
static char field_player_crates_found[5] = { "CRA?" };
static char field_player_harvested[5] = { "HRV?" };
static char *houses[] = {
"GDI",
"NOD",
"NUT",
"JUR",
"M01",
"M02",
"M03",
"M04",
"M05",
"M06"
};
CCDebugString ("C&C95 - In Send_Statistics_Packet.\n");
if (!PacketLater){
CCDebugString ("C&C95 - PacketLater is false.\n");
/*
** Field to identify this as C&C 95 internet game statistics packet
*/
if (Server){
stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO);
}else{
stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO);
}
/*
** Game ID. A unique game identifier assigned by WChat.
*/
stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID);
/*
** Start credits.
*/
stats.Add_Field(FIELD_START_CREDITS, (unsigned long)MPlayerCredits);
/*
** Bases (On/Off)
*/
stats.Add_Field(FIELD_BASES, MPlayerBases ? "ON" : "OFF");
/*
** Tiberium (On/Off)
*/
stats.Add_Field(FIELD_TIBERIUM, MPlayerTiberium ? "ON" : "OFF");
/*
** Crates (On/Off)
*/
stats.Add_Field(FIELD_CRATES, MPlayerGoodies ? "ON" : "OFF");
/*
** AI Players (On/Off/Capture the flag)
*/
stats.Add_Field(FIELD_AI_PLAYERS, MPlayerGhosts ? "ON" : "OFF");
stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF");
/*
** Start unit count
*/
stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)MPlayerUnitCount);
/*
** Tech level.
*/
stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel);
CCDebugString ("C&C95 - Adding stats field for scenario.\n");
/*
** Scenario
*/
char fname[128];
char namebuffer[40];
char *abuffer = (char *)_ShapeBuffer;
memset(abuffer, '\0', _ShapeBufferSize);
sprintf(fname,"%s.INI",ScenarioName);
CCFileClass fileo;
fileo.Set_Name (fname);
fileo.Read(abuffer, _ShapeBufferSize-1);
fileo.Close();
WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer);
stats.Add_Field(FIELD_SCENARIO, namebuffer);
//stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]);
/*
** Game completion status.
**
** Connection lost.
** Player 1 won
** Player 2 won
** Player 1 won by resignation
** Player 2 won by resignation
** Player 1 aborted
** Player 2 aborted
**
*/
CCDebugString ("C&C95 - Adding stats field for completion status.\n");
HouseClass *player1 = HouseClass::As_Pointer(MPlayerHouses[0]);
HouseClass *player2 = HouseClass::As_Pointer(MPlayerHouses[1]);
int completion = -1;
if (ConnectionLost){
completion = COMPLETION_CONNECTION_LOST;
CCDebugString ("C&C95 - Completion status is connection lost.\n");
}else{
if (player1 && player2){
if (player1->IGaveUp){
completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n");
}
if (player2->IGaveUp){
completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n");
}
if (player2->IsDefeated){
/*
** Player 1 won. Find out how.
*/
completion = COMPLETION_PLAYER_1_WON;
if (player2->Resigned){
completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION;
CCDebugString ("C&C95 - Completion status is player 2 resigned.\n");
}else{
if (player2->IGaveUp){
completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n");
}
}
}else{
if (player1->IsDefeated){
/*
** Player 2 won. Find out how.
*/
completion = COMPLETION_PLAYER_2_WON;
if (player1->Resigned){
completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION;
CCDebugString ("C&C95 - Completion status is player 1 resigned.\n");
}else{
if (player1->IGaveUp){
completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n");
}
}
}
}
}
}
stats.Add_Field (FIELD_COMPLETION, (char) completion);
/*
** Game start time (GMT or Pacific?)
**
** Passed from WChat
*/
stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime);
/*
** Game duration (seconds).
*/
stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60);
/*
** Avg. frame rate.
*/
if (GameEndTime/60 == 0){
stats.Add_Field (FIELD_FRAME_RATE, 0L );
}else{
stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) );
}
CCDebugString ("C&C95 - Adding hardware info stats.\n");
/*
** CPU type
*/
stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType);
/*
** Memory
*/
MEMORYSTATUS mem_info;
mem_info.dwLength=sizeof(mem_info);
GlobalMemoryStatus(&mem_info);
stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys);
/*
** Video memory
*/
DDCAPS video_capabilities;
long video_memory;
if (DirectDrawObject){
video_capabilities.dwSize = sizeof (video_capabilities);
if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){
video_memory = video_capabilities.dwVidMemTotal;
video_memory += 1024*1024 -1;
video_memory &= 0xfff00000;
stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory);
}
}
CCDebugString ("C&C95 - Adding game info stats.\n");
/*
** Game speed setting.
*/
stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed);
/*
** C&C 95 version/build date
*/
char version[128];
sprintf (version, "%d%s", Version_Number(), VersionText);
stats.Add_Field (FIELD_GAME_VERSION, version);
char path_to_exe[280];
FILETIME write_time; //File time is 64 bits
GetModuleFileName (ProgramInstance, path_to_exe, 280);
RawFileClass file;
file.Set_Name(path_to_exe);
file.Open();
HANDLE handle = file.Get_File_Handle();
if (handle != INVALID_HANDLE_VALUE){
if (GetFileTime (handle, NULL, NULL, &write_time)){
write_time.dwLowDateTime = htonl (write_time.dwLowDateTime);
write_time.dwHighDateTime = htonl (write_time.dwHighDateTime);
stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time));
}
}
/*
** Covert installed? (Yes/No)
*/
stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present());
CCDebugString ("C&C95 - Adding house specific stats.\n");
/*
** Build the player specific statistics
**
*/
for (int house = 0 ; house < 2 ; house++){
player = HouseClass::As_Pointer(MPlayerHouses[house]);
if (player){
/*
** Player handle.
*/
field_player_handle[3] = '1' + (char)house;
stats.Add_Field (field_player_handle, (char*) MPlayerNames[house]);
/*
** Player team. (NOD or GDI)
*/
field_player_team[3] = '1' + (char)house;
stats.Add_Field (field_player_team, houses[player->ActLike]);
/*
** Player color
*/
field_player_color[3] = '1' + (char)house;
stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1));
/*
** Player end credits.
*/
field_player_credits[3] = '1' + (char)house;
stats.Add_Field (field_player_credits, player->Credits + player->Tiberium);
/*
** Number of each unit/building type built
*/
field_player_infantry_bought[3] = '1' + (char)house;
field_player_units_bought[3] = '1' + (char)house;
field_player_planes_bought[3] = '1' + (char)house;
field_player_buildings_bought[3] = '1' + (char)house;
player->InfantryTotals->To_Network_Format();
player->UnitTotals->To_Network_Format();
player->AircraftTotals->To_Network_Format();
player->BuildingTotals->To_Network_Format();
stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
player->InfantryTotals->To_PC_Format();
player->UnitTotals->To_PC_Format();
player->AircraftTotals->To_PC_Format();
player->BuildingTotals->To_PC_Format();
/*
** Clear out the counts and use the space to count up the current number of units/buildings
*/
player->InfantryTotals->Clear_Unit_Total();
player->AircraftTotals->Clear_Unit_Total();
player->UnitTotals->Clear_Unit_Total();
player->BuildingTotals->Clear_Unit_Total();
/*
** Number of units remaining to player
*/
for (index = 0; index < Units.Count(); index++) {
UnitClass const * unit = Units.Ptr(index);
if (unit->House == player){
player->UnitTotals->Increment_Unit_Total (unit->Class->Type);
}
}
for (index = 0; index < Infantry.Count(); index++) {
InfantryClass const * infantry = Infantry.Ptr(index);
if (infantry->House == player && !infantry->Class->IsCivilian){
player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type);
}
}
for (index = 0; index < Aircraft.Count(); index++) {
AircraftClass const * aircraft = Aircraft.Ptr(index);
if (aircraft->House == player && aircraft->Class->Type != AIRCRAFT_CARGO){
player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type);
}
}
for (index = 0; index < Buildings.Count(); index++) {
BuildingClass const * building = Buildings.Ptr(index);
if (building->House == player){
player->BuildingTotals->Increment_Unit_Total (building->Class->Type);
}
}
player->InfantryTotals->To_Network_Format();
player->UnitTotals->To_Network_Format();
player->AircraftTotals->To_Network_Format();
player->BuildingTotals->To_Network_Format();
field_player_infantry_left[3] = '1' + (char)house;
field_player_units_left[3] = '1' + (char)house;
field_player_planes_left[3] = '1' + (char)house;
field_player_buildings_left[3] = '1' + (char)house;
stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
/*
** Number of enemy units/buildings of each type destroyed.
*/
player->DestroyedInfantry->To_Network_Format();
player->DestroyedUnits->To_Network_Format();
player->DestroyedAircraft->To_Network_Format();
player->DestroyedBuildings->To_Network_Format();
field_player_infantry_killed[3] = '1' + (char)house;
field_player_units_killed[3] = '1' + (char)house;
field_player_planes_killed[3] = '1' + (char)house;
field_player_buildings_killed[3] = '1' + (char)house;
stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4);
stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4);
stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4);
stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4);
/*
** Number and type of enemy buildings captured
*/
field_player_buildings_captured[3] = '1' + (char)house;
player->CapturedBuildings->To_Network_Format();
stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4);
/*
** Number of crates discovered and their contents
*/
field_player_crates_found[3] = '1' + (char)house;
player->TotalCrates->To_Network_Format();
stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4);
/*
** Amount of tiberium turned into credits
*/
field_player_harvested[3] = '1' + (char)house;
stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits);
}
}
CCDebugString ("C&C95 - Calling Create_Comms_Packet.\n");
/*
** Create the comms packet to be sent
*/
packet = stats.Create_Comms_Packet(packet_size);
CCDebugString ("C&C95 - Returned from Create_Comms_Packet.\n");
/*
** If a player disconnected then dont send the packet at this time - save it for later
*/
if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION
|| completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){
PacketLater = packet;
CCDebugString ("C&C95 - Flagging to send the packet later.\n");
return;
}
}else{ //else for if (!PacketLater)
CCDebugString ("C&C95 - PacketLater is true.\n");
/*
** Send the packet we calculated earlier when the disconnect occurred
*/
packet = PacketLater;
PacketLater = NULL;
}
/*
** Send it.....
*/
int times = 100; //100 times max
CountDownTimerClass send_timer;
CCDebugString ("C&C95 - About to send stats packet to DDE server.\n");
while ( ! Send_Data_To_DDE_Server ((char*)packet, packet_size, DDEServerClass::DDE_PACKET_GAME_RESULTS)){
CCDebugString ("C&C95 - Stats packet send failed.\n");
send_timer.Set (60, true);
while (send_timer.Time()){};
}
/*
** Save it to disk as well so I can see it
*/
#if (0)
RawFileClass anotherfile ("packet.net");
anotherfile.Write(packet, packet_size);
#endif //(0)
/*
** Tidy up
*/
CCDebugString ("C&C95 - About to delete packet memory.\n");
delete [] packet;
GameStatisticsPacketSent = true;
CCDebugString ("C&C95 - Returning from Send_Statistics_Packet.\n");
#endif //DEMO
#endif
}
void Register_Game_Start_Time(void)
{
GameTimer.Set (0, true);
GameTimerInUse = true;
}
extern void Register_Game_End_Time(void)
{
GameEndTime = GameTimer.Time();
GameTimerInUse = false;
}