782 lines
24 KiB
C++
782 lines
24 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/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** Confidential - Westwood Studios ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Commando/GameSpy_QnR.cpp $*
|
||
|
* *
|
||
|
* $Author:: Greg_h $*
|
||
|
* *
|
||
|
* $Modtime:: 7/08/02 5:55p $*
|
||
|
* *
|
||
|
* $Revision:: 17 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include <Gamespy\gs_patch_usage.h>
|
||
|
#include <Gamespy\gcdkeyserver.h>
|
||
|
#include "specialbuilds.h"
|
||
|
#include "dlgcncteaminfo.h"
|
||
|
#include "resource.h"
|
||
|
#include "listctrl.h"
|
||
|
#include "imagectrl.h"
|
||
|
#include "playertype.h"
|
||
|
#include "combat.h"
|
||
|
#include "teammanager.h"
|
||
|
#include "playermanager.h"
|
||
|
#include "player.h"
|
||
|
#include "soldier.h"
|
||
|
#include "gameinitmgr.h"
|
||
|
#include "gamemode.h"
|
||
|
#include "gamedata.h"
|
||
|
#include "input.h"
|
||
|
#include "healthbarctrl.h"
|
||
|
#include "basecontroller.h"
|
||
|
#include "building.h"
|
||
|
#include "damage.h"
|
||
|
#include "vehicle.h"
|
||
|
#include "assets.h"
|
||
|
#include "translatedb.h"
|
||
|
#include "WOLGMode.h"
|
||
|
#include <WWOnline\WOLUser.h>
|
||
|
#include "string_ids.h"
|
||
|
#include "mousemgr.h"
|
||
|
#include "directinput.h"
|
||
|
#include "GameSpy_QnR.h"
|
||
|
#include "verchk.h"
|
||
|
#include "buildnum.h"
|
||
|
#include "serversettings.h"
|
||
|
#include "consolemode.h"
|
||
|
#include "useroptions.h"
|
||
|
#include "gdcnc.h"
|
||
|
#include "rawfile.h"
|
||
|
#include "shellapi.h"
|
||
|
#include "netutil.h"
|
||
|
#include "gamespybanlist.h"
|
||
|
|
||
|
CGameSpyQnR GameSpyQnR;
|
||
|
|
||
|
#if defined(MULTIPLAYERDEMO)
|
||
|
const char *CGameSpyQnR::gamename = "ccrenegadedemo";
|
||
|
const char *CGameSpyQnR::bname = "Demo";
|
||
|
const int CGameSpyQnR::prodid = 10063;
|
||
|
const int CGameSpyQnR::cdkey_id = 0;
|
||
|
#elif defined(FREEDEDICATEDSERVER)
|
||
|
const char *CGameSpyQnR::bname = "FDS";
|
||
|
const char *CGameSpyQnR::gamename = "ccrenegade";
|
||
|
const int CGameSpyQnR::prodid = 10064;
|
||
|
const int CGameSpyQnR::cdkey_id = 577;
|
||
|
#elif defined(BETACLIENT)
|
||
|
const char *CGameSpyQnR::bname = "Beta";
|
||
|
const char *CGameSpyQnR::gamename = "ccrenegade";
|
||
|
const int CGameSpyQnR::prodid = 10064;
|
||
|
const int CGameSpyQnR::cdkey_id = 0;
|
||
|
#else
|
||
|
const char *CGameSpyQnR::bname = "Retail";
|
||
|
const char *CGameSpyQnR::gamename = "ccrenegade";
|
||
|
const int CGameSpyQnR::prodid = 10064;
|
||
|
const int CGameSpyQnR::cdkey_id = 577;
|
||
|
#endif
|
||
|
|
||
|
const char *CGameSpyQnR::default_heartbeat_list = "master.gamespy.com:27900, master.udpsoft.com:27900";
|
||
|
|
||
|
|
||
|
|
||
|
/*********
|
||
|
These c-style callback stubs are used by the SDK. The current game object
|
||
|
(passed in qr_init) is given in userdata, so we know what object to reference
|
||
|
**********/
|
||
|
|
||
|
void c_players_callback(char *outbuf, int maxlen, void *userdata)
|
||
|
{
|
||
|
((CGameSpyQnR *)userdata)->players_callback(outbuf, maxlen);
|
||
|
}
|
||
|
|
||
|
void c_rules_callback(char *outbuf, int maxlen, void *userdata)
|
||
|
{
|
||
|
((CGameSpyQnR *)userdata)->rules_callback(outbuf, maxlen);
|
||
|
}
|
||
|
|
||
|
void c_info_callback(char *outbuf, int maxlen, void *userdata)
|
||
|
{
|
||
|
((CGameSpyQnR *)userdata)->info_callback(outbuf, maxlen);
|
||
|
}
|
||
|
|
||
|
void c_basic_callback(char *outbuf, int maxlen, void *userdata)
|
||
|
{
|
||
|
((CGameSpyQnR *)userdata)->basic_callback(outbuf, maxlen);
|
||
|
}
|
||
|
|
||
|
/***********
|
||
|
A simple game object. Consists of some data and a main loop function.
|
||
|
***********/
|
||
|
CGameSpyQnR::CGameSpyQnR(void) : m_GSInit(FALSE), m_GSEnabled(FALSE)
|
||
|
{
|
||
|
// Secret keys removed per Security review requirements. LFeenanEA - 27th January 2025
|
||
|
|
||
|
//set the secret key, in a semi-obfuscated manner
|
||
|
// tY1S8q = FULL , LsEwS3 = DEMO
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
secret_key[3] = 'R'; if (secret_key[3])
|
||
|
secret_key[1] = 'E'; if (secret_key[1])
|
||
|
secret_key[0] = 'M'; if (secret_key[0])
|
||
|
secret_key[4] = 'O'; if (secret_key[4])
|
||
|
secret_key[5] = 'V'; if (secret_key[5])
|
||
|
secret_key[3] = 'E'; if (secret_key[3])
|
||
|
secret_key[2] = 'D'; if (secret_key[2])
|
||
|
secret_key[6] = '1'; if (secret_key[2])
|
||
|
secret_key[5] = '2'; if (secret_key[5])
|
||
|
secret_key[4] = '3';
|
||
|
#else
|
||
|
secret_key[5] = 'R'; if (secret_key[5])
|
||
|
secret_key[4] = 'E'; if (secret_key[4])
|
||
|
secret_key[6] = 'M'; if (secret_key[4])
|
||
|
secret_key[1] = 'O'; if (secret_key[1])
|
||
|
secret_key[5] = 'V'; if (secret_key[5])
|
||
|
secret_key[2] = 'E'; if (secret_key[2])
|
||
|
secret_key[3] = 'D'; if (secret_key[3])
|
||
|
secret_key[6] = '1'; if (secret_key[3])
|
||
|
secret_key[0] = '2'; if (secret_key[0])
|
||
|
secret_key[3] = '3';
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
CGameSpyQnR::~CGameSpyQnR()
|
||
|
{
|
||
|
Shutdown();
|
||
|
}
|
||
|
|
||
|
void CGameSpyQnR::LaunchArcade(void) {
|
||
|
char *akey = "Software\\GameSpy\\GameSpy Arcade";
|
||
|
BOOL launched = FALSE;
|
||
|
HKEY key = NULL;
|
||
|
int result = 0;
|
||
|
|
||
|
result = RegOpenKeyEx(HKEY_CURRENT_USER, akey, 0, KEY_READ, &key);
|
||
|
if (result == ERROR_SUCCESS) {
|
||
|
StringClass value(true);
|
||
|
//
|
||
|
// Get the size of the entry
|
||
|
//
|
||
|
DWORD data_size = 0;
|
||
|
DWORD type = 0;
|
||
|
result = ::RegQueryValueEx ((HKEY)key, "InstDir", NULL, &type, NULL, &data_size);
|
||
|
if (result == ERROR_SUCCESS && type == REG_SZ) {
|
||
|
|
||
|
//
|
||
|
// Read the entry from the registry
|
||
|
//
|
||
|
::RegQueryValueEx ((HKEY)key, "InstDir", NULL, &type,
|
||
|
(LPBYTE)value.Get_Buffer(data_size), &data_size);
|
||
|
}
|
||
|
if (!value.Is_Empty()) {
|
||
|
if (value[value.Get_Length()-1] == '\\') {
|
||
|
value += "Aphex.exe";
|
||
|
} else {
|
||
|
value += "\\Aphex.exe";
|
||
|
}
|
||
|
|
||
|
RawFileClass file(value);
|
||
|
if (file.Is_Available()) {
|
||
|
StringClass params("+svc ");
|
||
|
params += gamename;
|
||
|
if (((int)ShellExecute (NULL, "open", value, params, NULL, SW_SHOW)) > 32) {
|
||
|
launched = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!launched) {
|
||
|
char url[1000] = "";
|
||
|
|
||
|
#ifdef MULTIPLAYERDEMO
|
||
|
::strcpy(url, "http://www.gamespyarcade.com/features/launch.asp?svcname=ccrenegadedemo&distID=432");
|
||
|
#else
|
||
|
::strcpy(url, "http://www.gamespyarcade.com/features/launch.asp?svcname=ccrenegade&distID=391");
|
||
|
#endif
|
||
|
|
||
|
ShellExecute (NULL, "open", url, NULL, NULL, SW_SHOW);
|
||
|
}
|
||
|
}
|
||
|
void CGameSpyQnR::Shutdown(void) {
|
||
|
|
||
|
#ifndef BETACLIENT
|
||
|
if (m_GSInit) {
|
||
|
/*
|
||
|
We don't really need to set the mode to exiting here, since we immediately
|
||
|
send the statechanged heartbeat and kill off the query sockets
|
||
|
gamemode = "exiting";*/
|
||
|
ConsoleBox.Print("Shutting down GameSpy Q&R\n");
|
||
|
qr_send_exiting(query_reporting_rec);
|
||
|
qr_shutdown(query_reporting_rec);
|
||
|
m_GSEnabled = m_GSInit = false;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void CGameSpyQnR::TrackUsage(void) {
|
||
|
|
||
|
#ifndef WWDEBUG
|
||
|
char filename[MAX_PATH];
|
||
|
GetModuleFileName(NULL, filename, sizeof(filename));
|
||
|
VS_FIXEDFILEINFO version;
|
||
|
GetVersionInfo(filename, &version);
|
||
|
int ver = version.dwFileVersionMS;
|
||
|
|
||
|
StringClass b(true);
|
||
|
b.Format("%s %s V%d.%3.3d(%s-%d)", "Win-X86", bname, (ver&0xffff0000)>>16, ver&0xffff,
|
||
|
BuildInfoClass::Get_Builder_Initials(), BuildInfoClass::Get_Build_Number());
|
||
|
|
||
|
// Send off usage Tracking info to GameSpy
|
||
|
ptTrackUsage(0, prodid, b.Peek_Buffer(), (cUserOptions::Sku.Get()&0xff)+438, false);
|
||
|
#endif // WWDEBUG
|
||
|
}
|
||
|
|
||
|
void CGameSpyQnR::Init(void) {
|
||
|
|
||
|
#ifndef BETACLIENT
|
||
|
|
||
|
if (m_GSEnabled && !m_GSInit && The_Game() && The_Game()->Get_Game_Type() == cGameData::GAME_TYPE_CNC) {
|
||
|
|
||
|
ConsoleBox.Print("Initializing GameSpy Q&R\n");
|
||
|
|
||
|
BOOL test = FALSE;
|
||
|
// Init the GameSpy QnR engine
|
||
|
extern ULONG g_ip_override;
|
||
|
char ipstr[32];
|
||
|
char *ip = ipstr;
|
||
|
|
||
|
if (g_ip_override == INADDR_NONE) {
|
||
|
if (cUserOptions::PreferredGameSpyNic.Get()) {
|
||
|
strcpy(ip, cNetUtil::Address_To_String(cUserOptions::PreferredGameSpyNic.Get()));
|
||
|
} else {
|
||
|
ip = NULL;
|
||
|
}
|
||
|
} else {
|
||
|
strcpy(ip, cNetUtil::Address_To_String(g_ip_override));
|
||
|
}
|
||
|
|
||
|
if (!get_master_count()) {
|
||
|
GameSpyQnR.Parse_HeartBeat_List(Get_Default_HeartBeat_List());
|
||
|
}
|
||
|
test = qr_init(&query_reporting_rec, ip, cUserOptions::GameSpyQueryPort.Get(),
|
||
|
gamename, secret_key, c_basic_callback, c_info_callback, c_rules_callback,
|
||
|
c_players_callback, this);
|
||
|
WWASSERT(!test);
|
||
|
gcd_init_qr(query_reporting_rec, cdkey_id);
|
||
|
|
||
|
StartTime = time(NULL);
|
||
|
m_GSInit = TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*******
|
||
|
DoGameStuff
|
||
|
Simulate whatever else a game server does
|
||
|
********/
|
||
|
void CGameSpyQnR::DoGameStuff(void)
|
||
|
{
|
||
|
// Sleep(100);
|
||
|
}
|
||
|
|
||
|
#define BANLIST_RELOAD_TIME (1*60*1000)
|
||
|
|
||
|
/*******************
|
||
|
CGameSpyQnR::run
|
||
|
Simulates a main game loop
|
||
|
*****************/
|
||
|
void CGameSpyQnR::Think()
|
||
|
{
|
||
|
static DWORD stime = (DWORD)(0 - BANLIST_RELOAD_TIME);
|
||
|
static DWORD ttime = 0;
|
||
|
|
||
|
if (TIMEGETTIME() - stime > BANLIST_RELOAD_TIME) {
|
||
|
GameSpyBanList.LoadBans();
|
||
|
stime = TIMEGETTIME();
|
||
|
}
|
||
|
// check twice a second
|
||
|
if (TIMEGETTIME() - ttime > 500) {
|
||
|
GameSpyBanList.Think();
|
||
|
ttime = TIMEGETTIME();
|
||
|
}
|
||
|
|
||
|
#ifndef BETACLIENT
|
||
|
// DoGameStuff();
|
||
|
if (m_GSInit && m_GSEnabled && GameInitMgrClass::Is_LAN_Initialized() &&
|
||
|
!CombatManager::Is_Loading_Level()) {
|
||
|
qr_process_queries(query_reporting_rec);
|
||
|
gcd_think();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*************
|
||
|
basic_callback
|
||
|
sends a (sample) response to the basic query
|
||
|
includes the following keys:
|
||
|
\gamename\
|
||
|
\gamever\
|
||
|
\location\
|
||
|
*************/
|
||
|
void CGameSpyQnR::basic_callback(char *outbuf, int maxlen)
|
||
|
{
|
||
|
|
||
|
WWDEBUG_SAY(("-->GS_QnR -- Basic callback\n"));
|
||
|
WWASSERT(!CombatManager::Is_Loading_Level());
|
||
|
|
||
|
if (!maxlen || !outbuf) return;
|
||
|
|
||
|
outbuf[0] = 0;
|
||
|
|
||
|
StringClass b(true);
|
||
|
b.Format("%d", BuildInfoClass::Get_Build_Number());
|
||
|
sprintf(outbuf, "\\gamename\\%s\\gamever\\%s", gamename, b.Peek_Buffer());
|
||
|
// sprintf(outbuf, "\\gamename\\%s\\gamever\\%s\\location\\%d", gamename, b.Peek_Buffer(), 1);
|
||
|
|
||
|
#ifdef WWDEBUG
|
||
|
StringClass tstr(true);
|
||
|
tstr.Format("GS_QnR -- Basic callback, sent: %s\n",outbuf);
|
||
|
OutputDebugString(tstr.Peek_Buffer());
|
||
|
#endif
|
||
|
WWDEBUG_SAY(("<--GS_QnR -- Basic callback\n"));
|
||
|
|
||
|
// printf("Basic callback, sent: %s\n\n",outbuf);
|
||
|
}
|
||
|
|
||
|
/************
|
||
|
info_callback
|
||
|
Sends a (sample) response to the info query
|
||
|
including the following keys:
|
||
|
\hostname\
|
||
|
\hostport\
|
||
|
\mapname\
|
||
|
\gametype\
|
||
|
\numplayers\
|
||
|
\maxplayers\
|
||
|
\gamemode\
|
||
|
************/
|
||
|
void CGameSpyQnR::info_callback(char *outbuf, int maxlen)
|
||
|
{
|
||
|
|
||
|
WWDEBUG_SAY(("-->GS_QnR -- Info callback\n"));
|
||
|
WWASSERT(!CombatManager::Is_Loading_Level());
|
||
|
|
||
|
if (!maxlen || !outbuf) return;
|
||
|
|
||
|
outbuf[0] = 0;
|
||
|
|
||
|
while(1) {
|
||
|
|
||
|
StringClass value(true);
|
||
|
|
||
|
WideStringClass(The_Game()->Get_Game_Title()).Convert_To(value);
|
||
|
if (value.Get_Length() > 25) {
|
||
|
value[25] = 0;
|
||
|
}
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "hostname", value)) break;
|
||
|
|
||
|
value.Format("%d", The_Game()->Get_Port());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "hostport", value)) break;
|
||
|
|
||
|
value = The_Game()->Get_Map_Name();
|
||
|
char *s = value.Peek_Buffer();
|
||
|
char *t = strrchr(s, '.');
|
||
|
if (t) value.Erase(t-s, s+strlen(s)-t);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "mapname", value)) break;
|
||
|
|
||
|
value = The_Game()->Get_Mod_Name();
|
||
|
if (!value.Is_Empty()) {
|
||
|
s = value.Peek_Buffer();
|
||
|
t = strrchr(s, '.');
|
||
|
if (t) value.Erase(t-s, s+strlen(s)-t);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "gametype", value)) break;
|
||
|
} else {
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "gametype", "C&C")) break;
|
||
|
}
|
||
|
|
||
|
int pcount = 0;
|
||
|
for (SLNode<cPlayer> *player_node = cPlayerManager::Get_Player_Object_List ()->Head ()
|
||
|
; player_node != NULL; player_node = player_node->Next ()) {
|
||
|
|
||
|
cPlayer *player = player_node->Data ();
|
||
|
WWASSERT (player != NULL);
|
||
|
|
||
|
if (player->Get_Is_Active().Is_False()) {
|
||
|
// if (player->Get_Is_Active().Is_False() || !player->Is_Human()) {
|
||
|
continue;
|
||
|
}
|
||
|
pcount++;
|
||
|
}
|
||
|
|
||
|
value.Format("%d", pcount);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "numplayers", value)) break;
|
||
|
value.Format("%d", The_Game()->Get_Max_Players());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "maxplayers", value)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "gamemode", "openplaying")) break;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#ifdef WWDEBUG
|
||
|
StringClass tstr(true);
|
||
|
tstr.Format("GS_QnR -- Info callback, sent: %s\n",outbuf);
|
||
|
OutputDebugString(tstr.Peek_Buffer());
|
||
|
#endif
|
||
|
WWDEBUG_SAY(("<--GS_QnR -- Info callback\n"));
|
||
|
|
||
|
}
|
||
|
|
||
|
/***************
|
||
|
rules_callback
|
||
|
Sends a response to the rules query. You may
|
||
|
need to add custom fields for your game in here. Some are provided
|
||
|
as an example
|
||
|
The following rules are included:
|
||
|
\timelimit\
|
||
|
\fraglimit\
|
||
|
\teamplay\
|
||
|
\rankedserver\
|
||
|
****************/
|
||
|
void CGameSpyQnR::rules_callback(char *outbuf, int maxlen)
|
||
|
{
|
||
|
static StringClass b;
|
||
|
WWDEBUG_SAY(("-->GS_QnR -- Rules callback\n"));
|
||
|
WWASSERT(!CombatManager::Is_Loading_Level());
|
||
|
|
||
|
if (!maxlen || !outbuf) return;
|
||
|
|
||
|
outbuf[0] = 0;
|
||
|
|
||
|
while(1) {
|
||
|
|
||
|
StringClass value(true);
|
||
|
const char *zero = "0"; const char *one = "1";
|
||
|
|
||
|
// WideStringClass timelimit;
|
||
|
// The_Game()->Get_Time_Limit_Text(timelimit);
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "timeleft", timelimit)) break;
|
||
|
|
||
|
// value.Format("%d", The_Game()->Get_Time_Limit_Minutes());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "timelimit", value)) break;
|
||
|
|
||
|
// if (b.Is_Empty()) {
|
||
|
// char filename[MAX_PATH];
|
||
|
// GetModuleFileName(NULL, filename, sizeof(filename));
|
||
|
// VS_FIXEDFILEINFO version;
|
||
|
// GetVersionInfo(filename, &version);
|
||
|
// int ver = version.dwFileVersionMS;
|
||
|
//
|
||
|
// b.Format("%s %s V%d.%3.3d(%s-%d)", "Win-X86", bname, (ver&0xffff0000)>>16, ver&0xffff,
|
||
|
// BuildInfoClass::Get_Builder_Initials(), BuildInfoClass::Get_Build_Number());
|
||
|
// }
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "Version", b)) break;
|
||
|
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "ServerMOTD", WideStringClass(The_Game()->Get_Motd()))) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "OwnerName", The_Game()->Get_Owner())) break;
|
||
|
value.Format("%d", (int)cUserOptions::BandwidthBps.Get());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "BW", value)) break;
|
||
|
// int utime = time(NULL) - StartTime;
|
||
|
// value.Format("%d:%d:%d:%d", utime/60/60/24, (utime/60/60)%24, (utime/60)%60, utime%60);
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "Uptime", value)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "CSVR", ConsoleBox.Is_Exclusive() ? one : zero)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "DED", The_Game()->IsDedicated.Get() ? one : zero)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "DG", The_Game()->DriverIsAlwaysGunner.Get() ? one : zero)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "password", The_Game()->IsPassworded.Get() ? one : zero)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "TC", The_Game()->IsTeamChangingAllowed.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "WeaponSpawning", The_Game()->SpawnWeapons.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "TeamRemix", The_Game()->RemixTeams.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "BuildingRepair", The_Game()->CanRepairBuildings.Get() ? one : zero)) break;
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "FF", The_Game()->IsFriendlyFirePermitted.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "BaseDestructionEndsGame", The_Game()->As_Cnc()->BaseDestructionEndsGame.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "BeaconPlacementEndsGame", The_Game()->As_Cnc()->BeaconPlacementEndsGame.Get() ? one : zero)) break;
|
||
|
// value.Format("%d", (int)The_Game()->Get_Radar_Mode());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "RadarMode", value)) break;
|
||
|
value.Format("%d", The_Game()->As_Cnc()->Get_Starting_Credits());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, "SC", value)) break;
|
||
|
|
||
|
// cTeam * p_team;
|
||
|
// for (SLNode<cTeam> * objnode = cTeamManager::Get_Team_Object_List()->Head()
|
||
|
// ; objnode != NULL; objnode = objnode->Next()) {
|
||
|
//
|
||
|
// p_team = objnode->Data();
|
||
|
// WWASSERT(p_team != NULL);
|
||
|
//
|
||
|
// if (p_team->Get_Id() == PLAYERTYPE_GDI) {
|
||
|
// value.Format("%.0f", p_team->Get_Score());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "GDI_Score", value)) break;
|
||
|
// } else if (p_team->Get_Id() == PLAYERTYPE_NOD) {
|
||
|
// value.Format("%.0f", p_team->Get_Score());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "NOD_Score", value)) break;
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// These don't apply to LAN/GameSpy Games
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "ClanMatch", The_Game()->IsClanGame.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "Laddered", The_Game()->IsLaddered.Get() ? one : zero)) break;
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, "QuickMatch", The_Game()->Is_QuickMatch_Server() ? one : zero)) break;
|
||
|
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
#ifdef WWDEBUG
|
||
|
StringClass tstr(true);
|
||
|
tstr.Format("GS_QnR -- Rules callback, sent: %s\n",outbuf);
|
||
|
OutputDebugString(tstr.Peek_Buffer());
|
||
|
#endif
|
||
|
WWDEBUG_SAY(("<--GS_QnR -- Rules callback\n"));
|
||
|
|
||
|
}
|
||
|
|
||
|
BOOL CGameSpyQnR::Parse_HeartBeat_List(const char *list) {
|
||
|
|
||
|
BOOL master_added = false;
|
||
|
|
||
|
char *str = new char[strlen(list)+1];
|
||
|
strcpy(str, list);
|
||
|
|
||
|
char *t = str;
|
||
|
|
||
|
clear_master_list();
|
||
|
|
||
|
while (t) {
|
||
|
WORD port = 27900;
|
||
|
struct sockaddr_in taddr;
|
||
|
memset(&taddr, 0, sizeof(taddr));
|
||
|
taddr.sin_family = AF_INET;
|
||
|
char *q = strchr(t, ',');
|
||
|
if (q) *q++ = 0;
|
||
|
char *s = strchr(t, ':');
|
||
|
if (s) {
|
||
|
// Parse off the port value
|
||
|
*s++ = 0;
|
||
|
char *p = s;
|
||
|
if (atoi(p)) port = atoi(p);
|
||
|
}
|
||
|
// skip white space
|
||
|
while (*t == ' ' || *t == '\t') t++;
|
||
|
// process the address
|
||
|
if (*t && get_sockaddrin(t, port, &taddr, NULL)) {
|
||
|
add_master(&taddr);
|
||
|
master_added = true;
|
||
|
}
|
||
|
t = q;
|
||
|
}
|
||
|
|
||
|
delete [] str;
|
||
|
|
||
|
if (!master_added) {
|
||
|
ConsoleBox.Print("Error processing HeartBeat List: <%s>\n", list);
|
||
|
ConsoleBox.Print("Assigning default HeartBeat List\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
BOOL CGameSpyQnR::Append_InfoKey_Pair(char *outbuf, int maxlen, const char *key, const char *value) {
|
||
|
|
||
|
WWASSERT(value);
|
||
|
WWASSERT(outbuf);
|
||
|
WWASSERT(key);
|
||
|
|
||
|
int clen = strlen(outbuf);
|
||
|
|
||
|
if (clen + strlen(key) + strlen(value) + 3 > (unsigned int)maxlen) return FALSE;
|
||
|
|
||
|
char *s = new char[strlen(value)+1];
|
||
|
strcpy(s, value);
|
||
|
char *t = s;
|
||
|
while (*t) {
|
||
|
if (*t == '\\') *t = '/';
|
||
|
t++;
|
||
|
}
|
||
|
t = strtrim(s);
|
||
|
sprintf(&outbuf[clen], "\\%s\\%s", key, t);
|
||
|
delete [] s;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL CGameSpyQnR::Append_InfoKey_Pair(char *outbuf, int maxlen, const char *key, const WideStringClass &value) {
|
||
|
static StringClass text;
|
||
|
|
||
|
value.Convert_To(text);
|
||
|
return Append_InfoKey_Pair(outbuf, maxlen, key, text.Peek_Buffer());
|
||
|
}
|
||
|
|
||
|
BOOL CGameSpyQnR::Append_InfoKey_Pair(char *outbuf, int maxlen, const char *key, const StringClass &value) {
|
||
|
|
||
|
return Append_InfoKey_Pair(outbuf, maxlen, key, value.Peek_Buffer());
|
||
|
}
|
||
|
|
||
|
/***************
|
||
|
players_callback
|
||
|
sends the players and their information.
|
||
|
Note that \ characters are not stripped out of player names. If
|
||
|
your game allows players or team names with the \ character, you will need
|
||
|
to strip or change it here.
|
||
|
The following keys are included for each player:
|
||
|
\player_N\
|
||
|
\frags_N\
|
||
|
\deaths_N\
|
||
|
\skill_N\
|
||
|
\ping_N\
|
||
|
\team_N\
|
||
|
***************/
|
||
|
void CGameSpyQnR::players_callback(char *outbuf, int maxlen)
|
||
|
{
|
||
|
|
||
|
// Send the minimum for now to reduce Bandwidth usage.
|
||
|
outbuf[0] = 0;
|
||
|
return;
|
||
|
|
||
|
|
||
|
|
||
|
int pindex = 0;
|
||
|
|
||
|
WWDEBUG_SAY(("-->GS_QnR -- Players callback\n"));
|
||
|
WWASSERT(!CombatManager::Is_Loading_Level());
|
||
|
|
||
|
if (!maxlen || !outbuf) return;
|
||
|
|
||
|
outbuf[0] = 0;
|
||
|
|
||
|
for (SLNode<cPlayer> *player_node = cPlayerManager::Get_Player_Object_List ()->Head ();
|
||
|
player_node != NULL;
|
||
|
player_node = player_node->Next ()) {
|
||
|
|
||
|
cPlayer *player = player_node->Data ();
|
||
|
WWASSERT (player != NULL);
|
||
|
|
||
|
if (player->Get_Is_Active().Is_False()) {
|
||
|
// if (player->Get_Is_Active().Is_False() || !player->Is_Human()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
StringClass keyval(true);
|
||
|
StringClass value(true);
|
||
|
|
||
|
// Set the Player's Name [Team]
|
||
|
value = player->Get_Name();
|
||
|
if (player->Get_Player_Type() == PLAYERTYPE_NOD) {
|
||
|
value += " [NOD]";
|
||
|
} else if (player->Get_Player_Type() == PLAYERTYPE_GDI) {
|
||
|
value += " [GDI]";
|
||
|
}
|
||
|
|
||
|
keyval.Format("player_%d", pindex);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
// Set the Player's Score
|
||
|
keyval.Format("frags_%d", pindex);
|
||
|
value.Format("%.0f", player->Get_Score());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
// Set the Player's Credits
|
||
|
// keyval.Format("credits_%d", pindex);
|
||
|
// value.Format("%.0f", player->Get_Money());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
// Set the Player's Ping to Server
|
||
|
keyval.Format("ping_%d", pindex);
|
||
|
value.Format("%d", player->Get_Ping());
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
// Set the Player's Kills
|
||
|
// keyval.Format("kills_%d", pindex);
|
||
|
// value.Format("%d", player->Get_Kills());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
// Set the Player's Deaths
|
||
|
// keyval.Format("deaths_%d", pindex);
|
||
|
// value.Format("%d", player->Get_Deaths());
|
||
|
// if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), value)) break;
|
||
|
|
||
|
/* SmartGameObj *game_obj = player->Get_GameObj ();
|
||
|
if (game_obj != NULL && game_obj->As_SoldierGameObj () != NULL) {
|
||
|
|
||
|
// Set the Player's Class (ie: Technician,Sakura,Havok)
|
||
|
keyval.Format("class_%d", pindex);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(),
|
||
|
WideStringClass(TRANSLATE(game_obj->Get_Translated_Name_ID())) )) break;
|
||
|
|
||
|
SoldierGameObj *soldier = game_obj->As_SoldierGameObj();
|
||
|
VehicleGameObj *vehicle = soldier->Get_Vehicle ();
|
||
|
|
||
|
// If they're in a vehicle set the vehicle name
|
||
|
keyval.Format("vehicle_%d", pindex);
|
||
|
if (vehicle != NULL) {
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(),
|
||
|
WideStringClass(TRANSLATE(vehicle->Get_Translated_Name_ID())) )) break;
|
||
|
} else {
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), "None")) break;
|
||
|
}
|
||
|
} else {
|
||
|
// If there's no gameobj then set vehicle/class to unknown
|
||
|
keyval.Format("class_%d", pindex);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), "Unknown")) break;
|
||
|
keyval.Format("vehicle_%d", pindex);
|
||
|
if (!Append_InfoKey_Pair(outbuf, maxlen, keyval.Peek_Buffer(), "Unknown")) break;
|
||
|
} */
|
||
|
|
||
|
pindex++;
|
||
|
|
||
|
}
|
||
|
|
||
|
#ifdef WWDEBUG
|
||
|
StringClass tstr(true);
|
||
|
tstr.Format("GS_QnR -- Players callback, sent: %s\n",outbuf);
|
||
|
OutputDebugString(tstr.Peek_Buffer());
|
||
|
#endif
|
||
|
|
||
|
WWDEBUG_SAY(("<--GS_QnR -- Players callback\n"));
|
||
|
return;
|
||
|
}
|
||
|
/************
|
||
|
We'll actually start up two completely seperate "game servers"
|
||
|
Each one initializes the Query & Reporting SDK and calls processs on it during
|
||
|
their main loop
|
||
|
************/
|
||
|
/*
|
||
|
int main(int argc, char* argv[])
|
||
|
{
|
||
|
CGameSpyQnR mygame1("Test Game Server 1"), mygame2("Test Game Server 2");
|
||
|
|
||
|
srand( GetTickCount() );
|
||
|
|
||
|
printf("Press any key to quit\n");
|
||
|
while (!_kbhit())
|
||
|
{
|
||
|
mygame1.run();
|
||
|
mygame2.run();
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
*/
|