416 lines
12 KiB
C++
416 lines
12 KiB
C++
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program 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.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* FILE
|
||
|
* $Archive: /Commando/Code/Commando/GameResSend.cpp $
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* PROGRAMMER
|
||
|
* $Author: Denzil_l $
|
||
|
*
|
||
|
* VERSION INFO
|
||
|
* $Revision: 25 $
|
||
|
* $Modtime: 1/14/02 10:40a $
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
#include "GameResSend.h"
|
||
|
#include "GameData.h"
|
||
|
#include "Player.h"
|
||
|
#include "consolemode.h"
|
||
|
#include <Combat\PlayerType.h>
|
||
|
#include <WWOnline\GameResPacket.h>
|
||
|
#include <WWOnline\WOLSession.h>
|
||
|
#include <WWOnline\WOLProduct.h>
|
||
|
#include <WWOnline\WOLUser.h>
|
||
|
#include <WWLib\CPUDetect.h>
|
||
|
#include <WWLib\VerChk.h>
|
||
|
#include <WWLib\CPUDetect.h>
|
||
|
#include <WWLib\global.h>
|
||
|
#include <WWLib\md5.h>
|
||
|
#include <WW3D2\DX8Wrapper.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
using namespace WWOnline;
|
||
|
|
||
|
static void AddPlayerStats(GameResPacket& stats, cPlayer* player, WOL::Locale locale,
|
||
|
int winnerID, bool teamGame);
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* SendGameResults
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Send game results for ladder ranking.
|
||
|
*
|
||
|
* INPUTS
|
||
|
* TheGame - The game played.
|
||
|
* Players - List of players.
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void SendGameResults(unsigned long gameID, cGameData* theGame, SList<cPlayer>* playerList)
|
||
|
{
|
||
|
RefPtr<WWOnline::Session> session = WWOnline::Session::GetInstance(false);
|
||
|
|
||
|
if (!session.IsValid())
|
||
|
{
|
||
|
assert(session.IsValid() && "SendGameResults() WOLSession not instantiated.");
|
||
|
WWDEBUG_SAY(("ERROR: SendGameResults() WOLSession not instantiated.\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
// Gather game information
|
||
|
//---------------------------------------------------------------------------
|
||
|
GameResPacket stats;
|
||
|
|
||
|
// Unique identifier for this game session.
|
||
|
stats.Add_Field("IDNO", gameID);
|
||
|
|
||
|
// Product SKU
|
||
|
RefPtr<Product> product = Product::Current();
|
||
|
unsigned long gameSKU = product->GetSKU();
|
||
|
stats.Add_Field("GSKU", gameSKU);
|
||
|
|
||
|
// Version of executable.
|
||
|
char filename[MAX_PATH];
|
||
|
GetModuleFileName(NULL, filename, sizeof(filename));
|
||
|
VS_FIXEDFILEINFO version;
|
||
|
GetVersionInfo(filename, &version);
|
||
|
stats.Add_Field("VERS", version.dwFileVersionMS);
|
||
|
|
||
|
// Executable build date
|
||
|
FILETIME createTime;
|
||
|
GetFileCreationTime(filename, &createTime);
|
||
|
|
||
|
SYSTEMTIME time;
|
||
|
FileTimeToSystemTime(&createTime, &time);
|
||
|
|
||
|
char buildDate[20];
|
||
|
sprintf(buildDate, "%02d/%02d/%04d %02d:%02d:%02d",
|
||
|
time.wMonth, time.wDay, time.wYear, time.wHour, time.wMinute, time.wSecond);
|
||
|
stats.Add_Field("DATE", buildDate);
|
||
|
|
||
|
// Proocessor information
|
||
|
stats.Add_Field("PROC", (char*)CPUDetectClass::Get_Processor_String());
|
||
|
stats.Add_Field("PSPD", (unsigned long)CPUDetectClass::Get_Processor_Speed());
|
||
|
|
||
|
// Amount of system memory on server
|
||
|
MEMORYSTATUS memStatus;
|
||
|
GlobalMemoryStatus(&memStatus);
|
||
|
stats.Add_Field("SMEM", (unsigned long)memStatus.dwTotalPhys);
|
||
|
|
||
|
// Video card information
|
||
|
DWORD cardInfo[4];
|
||
|
if (ConsoleBox.Is_Exclusive()) {
|
||
|
strcpy((char*)&cardInfo[0], "ConsoleMode");
|
||
|
} else {
|
||
|
const D3DADAPTER_IDENTIFIER8& adapter = DX8Wrapper::Get_Current_Adapter_Identifier();
|
||
|
cardInfo[0] = adapter.VendorId;
|
||
|
cardInfo[1] = adapter.DeviceId;
|
||
|
cardInfo[2] = adapter.SubSysId;
|
||
|
cardInfo[3] = adapter.Revision;
|
||
|
}
|
||
|
stats.Add_Field("SVID", (void*)cardInfo, sizeof(cardInfo));
|
||
|
|
||
|
// WOL Name of server
|
||
|
const WideStringClass& owner = theGame->Get_Owner();
|
||
|
StringClass serverName(true);
|
||
|
owner.Convert_To(serverName);
|
||
|
stats.Add_Field("SNAM", (char*)serverName.Peek_Buffer());
|
||
|
|
||
|
// Type of game played (Deathmatch, Capture the flag, CNC...)
|
||
|
// char* gameMode = (char*)theGame->Get_Game_Type_Name();
|
||
|
stats.Add_Field("MODE", "CNC");
|
||
|
|
||
|
// Name of the map used.
|
||
|
char *map_name = (char*)(theGame->Get_Map_Name().Peek_Buffer());
|
||
|
stats.Add_Field("GMAP", map_name);
|
||
|
|
||
|
// Was this a dedicated server?
|
||
|
bool dedicatedServer = theGame->IsDedicated.Get();
|
||
|
stats.Add_Field("DSVR", (char)dedicatedServer);
|
||
|
|
||
|
// Time game started
|
||
|
LPSYSTEMTIME gameTime = theGame->Get_Game_Start_Time();
|
||
|
char startTime[20];
|
||
|
sprintf(startTime, "%02d/%02d/%04d %02d:%02d:%02d",
|
||
|
gameTime->wMonth, gameTime->wDay, gameTime->wYear, gameTime->wHour, gameTime->wMinute, gameTime->wSecond);
|
||
|
stats.Add_Field("TIME", startTime);
|
||
|
|
||
|
// Duration of game
|
||
|
unsigned long duration = theGame->Get_Duration_Seconds();
|
||
|
stats.Add_Field("DURA", duration);
|
||
|
|
||
|
// Average FPS
|
||
|
unsigned long fps = theGame->Get_Frame_Count();
|
||
|
|
||
|
if (duration > 1)
|
||
|
{
|
||
|
fps = (fps / duration);
|
||
|
}
|
||
|
|
||
|
stats.Add_Field("AFPS", fps);
|
||
|
|
||
|
// Type of tournament
|
||
|
/*
|
||
|
char gameType = 'I';
|
||
|
|
||
|
if (theGame->Is_Team_Game())
|
||
|
{
|
||
|
gameType = 'T';
|
||
|
}
|
||
|
*/
|
||
|
char gameType = 'T';
|
||
|
|
||
|
if (theGame->IsClanGame.Is_True())
|
||
|
{
|
||
|
gameType = 'C';
|
||
|
}
|
||
|
|
||
|
char ranked = (theGame->IsLaddered.Is_True() == true) ? 'Y' : 'N';
|
||
|
|
||
|
char tournament[5];
|
||
|
sprintf(tournament, "%c%c ", gameType, ranked);
|
||
|
stats.Add_Field("TRNY", tournament);
|
||
|
|
||
|
// Include clan information
|
||
|
if (theGame->IsClanGame.Is_True())
|
||
|
{
|
||
|
unsigned long winningClan = 0;
|
||
|
unsigned long losingClan = 0;
|
||
|
|
||
|
int winner = theGame->Get_Winner_ID();
|
||
|
|
||
|
SLNode<cPlayer>* playerNode = playerList->Head();
|
||
|
|
||
|
for (int index = 0; index < playerList->Get_Count(); index++)
|
||
|
{
|
||
|
cPlayer* player = playerNode->Data();
|
||
|
|
||
|
if (player->Is_Human())
|
||
|
{
|
||
|
RefPtr<UserData> user = session->FindUser(player->Get_Name());
|
||
|
|
||
|
if (user.IsValid() && (user->GetSquadID() != 0))
|
||
|
{
|
||
|
int playerType = player->Get_Player_Type();
|
||
|
|
||
|
if ((playerType == winner) && (winningClan == 0))
|
||
|
{
|
||
|
winningClan = user->GetSquadID();
|
||
|
}
|
||
|
else if (losingClan == 0)
|
||
|
{
|
||
|
losingClan = user->GetSquadID();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stats.Add_Field("CLN1", winningClan);
|
||
|
stats.Add_Field("CLN2", losingClan);
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
// Player information
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
// Determine the number of players in the game
|
||
|
unsigned long numPlayers = 0;
|
||
|
SLNode<cPlayer>* playerNode = playerList->Head();
|
||
|
|
||
|
for (int index = 0; index < playerList->Get_Count(); index++)
|
||
|
{
|
||
|
cPlayer* player = playerNode->Data();
|
||
|
|
||
|
if (player->Is_Human())
|
||
|
{
|
||
|
numPlayers++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stats.Add_Field("PLRS", numPlayers);
|
||
|
|
||
|
// Gather per-player information
|
||
|
int winnerID = theGame->Get_Winner_ID();
|
||
|
playerNode = playerList->Head();
|
||
|
|
||
|
for (index = 0; index < playerList->Get_Count(); index++)
|
||
|
{
|
||
|
cPlayer* player = playerNode->Data();
|
||
|
|
||
|
if (player)
|
||
|
{
|
||
|
const WideStringClass& playerName = player->Get_Name();
|
||
|
|
||
|
// Player Location
|
||
|
WOL::Locale locale = WOL::LOC_UNKNOWN;
|
||
|
|
||
|
RefPtr<UserData> user = session->FindUser((const WCHAR*)playerName);
|
||
|
|
||
|
if (user.IsValid())
|
||
|
{
|
||
|
locale = user->GetLocale();
|
||
|
}
|
||
|
|
||
|
AddPlayerStats(stats, player, locale, winnerID, true);
|
||
|
}
|
||
|
|
||
|
playerNode = playerNode->Next();
|
||
|
}
|
||
|
|
||
|
unsigned long packetSize = 0;
|
||
|
unsigned long sig_offset = 0;
|
||
|
unsigned char* packet = stats.Create_Comms_Packet(packetSize, NULL, sig_offset);
|
||
|
|
||
|
WWDEBUG_SAY(("Sending game results packet. Size = %lu\n", packetSize));
|
||
|
|
||
|
#if(0)
|
||
|
#ifdef _DEBUG
|
||
|
HANDLE file = CreateFile("GameRes.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||
|
|
||
|
if (INVALID_HANDLE_VALUE != file)
|
||
|
{
|
||
|
// Write generic contents
|
||
|
DWORD written;
|
||
|
WriteFile(file, packet, packetSize, &written, NULL);
|
||
|
CloseHandle(file);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WWDEBUG_SAY(("Failed to create GameRes.dat file."));
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
session->SendGameResults(packet, packetSize);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* AddPlayerStats
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* Stats - Game result packet to add player stats to.
|
||
|
* Player - Player to add stats for.
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void AddPlayerStats(GameResPacket& stats, cPlayer* player, WOL::Locale locale,
|
||
|
int winnerID, bool isTeamed)
|
||
|
{
|
||
|
if (player->Is_Human())
|
||
|
{
|
||
|
// Players WOL name
|
||
|
const WideStringClass& playerName = player->Get_Name();
|
||
|
char name[10];
|
||
|
int len = wcstombs(name, (const WCHAR*)playerName, 10);
|
||
|
name[len] = 0;
|
||
|
|
||
|
stats.Add_Field("PNAM", name);
|
||
|
stats.Add_Field("PLOC", (unsigned long)locale);
|
||
|
|
||
|
int playerType = player->Get_Player_Type();
|
||
|
|
||
|
// Team (bit 31:win/lose, bits 7-0:team (0 = none, 1 = GDI, 2= NOD)
|
||
|
unsigned long team = 0;
|
||
|
|
||
|
if (isTeamed)
|
||
|
{
|
||
|
if (playerType == PLAYERTYPE_GDI)
|
||
|
{
|
||
|
team = 1;
|
||
|
}
|
||
|
else if (playerType == PLAYERTYPE_NOD)
|
||
|
{
|
||
|
team = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((isTeamed && playerType == winnerID) || (!isTeamed && player->Get_Id() == winnerID))
|
||
|
{
|
||
|
team |= 0x80000000;
|
||
|
}
|
||
|
|
||
|
stats.Add_Field("TEAM", team);
|
||
|
|
||
|
// Score and other information
|
||
|
unsigned long score = (unsigned long)max<int>(player->Get_Score(), 0);
|
||
|
stats.Add_Field("PSCR", score);
|
||
|
|
||
|
stats.Add_Field("PPTS", (long)player->Get_Ladder_Points());
|
||
|
stats.Add_Field("PTIM", (unsigned long)player->Get_Game_Time());
|
||
|
stats.Add_Field("PHLT", (unsigned long)player->Get_Final_Health());
|
||
|
stats.Add_Field("PKIL", (unsigned long)player->Get_Deaths());
|
||
|
stats.Add_Field("EKIL", (unsigned long)player->Get_Enemies_Killed());
|
||
|
stats.Add_Field("AKIL", (unsigned long)player->Get_Allies_Killed());
|
||
|
stats.Add_Field("SHOT", (unsigned long)player->Get_Shots_Fired());
|
||
|
stats.Add_Field("HEDF", (unsigned long)player->Get_Head_Shots());
|
||
|
stats.Add_Field("TORF", (unsigned long)player->Get_Torso_Shots());
|
||
|
stats.Add_Field("ARMF", (unsigned long)player->Get_Arm_Shots());
|
||
|
stats.Add_Field("LEGF", (unsigned long)player->Get_Leg_Shots());
|
||
|
stats.Add_Field("CRTF", (unsigned long)player->Get_Crotch_Shots());
|
||
|
stats.Add_Field("PUPS", (unsigned long)player->Get_Powerups_Collected());
|
||
|
stats.Add_Field("VKIL", (unsigned long)player->Get_Vehiclies_Destroyed());
|
||
|
stats.Add_Field("VTIM", (unsigned long)player->Get_Vehicle_Time());
|
||
|
stats.Add_Field("NKFV", (unsigned long)player->Get_Kills_From_Vehicle());
|
||
|
stats.Add_Field("SQUI", (unsigned long)player->Get_Squishes());
|
||
|
stats.Add_Field("PCRD", (unsigned long)player->Get_Credit_Grant());
|
||
|
stats.Add_Field("BKIL", (unsigned long)player->Get_Building_Destroyed());
|
||
|
stats.Add_Field("HEDR", (unsigned long)player->Get_Head_Hit());
|
||
|
stats.Add_Field("TORR", (unsigned long)player->Get_Torso_Hit());
|
||
|
stats.Add_Field("ARMR", (unsigned long)player->Get_Arm_Hit());
|
||
|
stats.Add_Field("LEGR", (unsigned long)player->Get_Leg_Hit());
|
||
|
stats.Add_Field("CRTR", (unsigned long)player->Get_Crotch_Hit());
|
||
|
stats.Add_Field("FLGC", (unsigned long)0);//no more CTF! (unsigned long)player->Get_Flag_Caps());
|
||
|
|
||
|
// Weapon usage
|
||
|
int numWeapons = min<int>(255, player->Get_Weapon_Fired_Count());
|
||
|
|
||
|
for (int wepIndex = 0; wepIndex < numWeapons; wepIndex++)
|
||
|
{
|
||
|
unsigned long weaponInfo[2] = {0,0};
|
||
|
player->Get_Weapon_Fired(wepIndex, weaponInfo[0], weaponInfo[1]);
|
||
|
|
||
|
char token[5];
|
||
|
sprintf(token, "WP%02X", wepIndex);
|
||
|
stats.Add_Field(token, (void*)weaponInfo, sizeof(weaponInfo));
|
||
|
}
|
||
|
}
|
||
|
}
|