/*
** Command & Conquer Generals(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 .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
// FILE: MainMenuUtils.cpp
// Author: Matthew D. Campbell, Sept 2002
// Description: GameSpy version check, patch download, etc utils
///////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include
//#include "Common/Registry.h"
#include "Common/UserPreferences.h"
#include "Common/Version.h"
#include "GameClient/GameText.h"
#include "GameClient/MessageBox.h"
#include "GameClient/Shell.h"
#include "GameLogic/ScriptEngine.h"
#include "GameClient/ShellHooks.h"
#include "GameSpy/ghttp/ghttp.h"
#include "GameNetwork/DownloadManager.h"
#include "GameNetwork/GameSpy/BuddyThread.h"
#include "GameNetwork/GameSpy/MainMenuUtils.h"
#include "GameNetwork/GameSpy/PeerDefs.h"
#include "GameNetwork/GameSpy/PeerThread.h"
#include "WWDownload/Registry.h"
#include "WWDownload/URLBuilder.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////
static Bool checkingForPatchBeforeGameSpy = FALSE;
static Int checksLeftBeforeOnline = 0;
static Int timeThroughOnline = 0; // used to avoid having old callbacks cause problems
static Bool mustDownloadPatch = FALSE;
static Bool cantConnectBeforeOnline = FALSE;
static std::list queuedDownloads;
static char *MOTDBuffer = NULL;
static char *configBuffer = NULL;
GameWindow *onlineCancelWindow = NULL;
static Bool s_asyncDNSThreadDone = TRUE;
static Bool s_asyncDNSThreadSucceeded = FALSE;
static Bool s_asyncDNSLookupInProgress = FALSE;
static HANDLE s_asyncDNSThreadHandle = NULL;
enum {
LOOKUP_INPROGRESS,
LOOKUP_FAILED,
LOOKUP_SUCCEEDED,
};
///////////////////////////////////////////////////////////////////////////////////////
static void startOnline( void );
static void reallyStartPatchCheck( void );
///////////////////////////////////////////////////////////////////////////////////////
// someone has hit a button allowing downloads to start
void StartDownloadingPatches( void )
{
if (queuedDownloads.empty())
{
HandleCanceledDownload();
return;
}
WindowLayout *layout;
layout = TheWindowManager->winCreateLayout( AsciiString( "Menus/DownloadMenu.wnd" ) );
layout->runInit();
layout->hide( FALSE );
layout->bringForward();
HandleCanceledDownload(FALSE);
DEBUG_ASSERTCRASH(TheDownloadManager, ("No download manager!"));
if (TheDownloadManager)
{
std::list::iterator it = queuedDownloads.begin();
while (it != queuedDownloads.end())
{
QueuedDownload q = *it;
TheDownloadManager->queueFileForDownload(q.server, q.userName, q.password,
q.file, q.localFile, q.regKey, q.tryResume);
queuedDownloads.pop_front();
it = queuedDownloads.begin();
}
TheDownloadManager->downloadNextQueuedFile();
}
}
///////////////////////////////////////////////////////////////////////////////////////
// user agrees to patch before going online
static void patchBeforeOnlineCallback( void )
{
StartDownloadingPatches();
}
// user doesn't want to patch before going online
static void noPatchBeforeOnlineCallback( void )
{
queuedDownloads.clear();
if (mustDownloadPatch || cantConnectBeforeOnline)
{
// go back to normal
HandleCanceledDownload();
}
else
{
// clear out unneeded downloads and go on
startOnline();
}
}
///////////////////////////////////////////////////////////////////////////////////////
static Bool hasWriteAccess()
{
const char* filename = "PatchAccessTest.txt";
remove(filename);
int handle = _open( filename, _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE);
if (handle == -1)
{
return false;
}
_close(handle);
remove(filename);
unsigned int val;
if (!GetUnsignedIntFromRegistry("", "Version", val))
{
return false;
}
if (!SetUnsignedIntInRegistry("", "Version", val))
{
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////
static void startOnline( void )
{
checkingForPatchBeforeGameSpy = FALSE;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline==0, ("starting online with pending callbacks"));
if (onlineCancelWindow)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
if (cantConnectBeforeOnline)
{
MessageBoxOk(TheGameText->fetch("GUI:CannotConnectToServservTitle"),
TheGameText->fetch("GUI:CannotConnectToServserv"),
noPatchBeforeOnlineCallback);
return;
}
if (queuedDownloads.size())
{
if (!hasWriteAccess())
{
MessageBoxOk(TheGameText->fetch("GUI:Error"),
TheGameText->fetch("GUI:MustHaveAdminRights"),
noPatchBeforeOnlineCallback);
}
else if (mustDownloadPatch)
{
MessageBoxOkCancel(TheGameText->fetch("GUI:PatchAvailable"),
TheGameText->fetch("GUI:MustPatchForOnline"),
patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
}
else
{
MessageBoxYesNo(TheGameText->fetch("GUI:PatchAvailable"),
TheGameText->fetch("GUI:CanPatchForOnline"),
patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
}
return;
}
TheScriptEngine->signalUIInteract(TheShellHookNames[SHELL_SCRIPT_HOOK_MAIN_MENU_ONLINE_SELECTED]);
DEBUG_ASSERTCRASH( !TheGameSpyBuddyMessageQueue, ("TheGameSpyBuddyMessageQueue exists!") );
DEBUG_ASSERTCRASH( !TheGameSpyPeerMessageQueue, ("TheGameSpyPeerMessageQueue exists!") );
DEBUG_ASSERTCRASH( !TheGameSpyInfo, ("TheGameSpyInfo exists!") );
SetUpGameSpy(MOTDBuffer, configBuffer);
if (MOTDBuffer)
{
delete[] MOTDBuffer;
MOTDBuffer = NULL;
}
if (configBuffer)
{
delete[] configBuffer;
configBuffer = NULL;
}
#ifdef ALLOW_NON_PROFILED_LOGIN
UserPreferences pref;
pref.load("GameSpyLogin.ini");
UserPreferences::const_iterator it = pref.find("useProfiles");
if (it != pref.end() && it->second.compareNoCase("yes") == 0)
#endif ALLOW_NON_PROFILED_LOGIN
TheShell->push( AsciiString("Menus/GameSpyLoginProfile.wnd") );
#ifdef ALLOW_NON_PROFILED_LOGIN
else
TheShell->push( AsciiString("Menus/GameSpyLoginQuick.wnd") );
#endif ALLOW_NON_PROFILED_LOGIN
}
///////////////////////////////////////////////////////////////////////////////////////
static void queuePatch(Bool mandatory, AsciiString downloadURL)
{
QueuedDownload q;
Bool success = TRUE;
AsciiString connectionType;
success &= downloadURL.nextToken(&connectionType, ":");
AsciiString server;
success &= downloadURL.nextToken(&server, ":/");
AsciiString user;
success &= downloadURL.nextToken(&user, ":@");
AsciiString pass;
success &= downloadURL.nextToken(&pass, "@/");
AsciiString filePath;
success &= downloadURL.nextToken(&filePath, "");
if (!success && user.isNotEmpty())
{
// no user/pass combo - move the file into it's proper place
filePath = user;
user = ""; // LFeenanEA - Credentials removed as per Security requirements
pass = "";
success = TRUE;
}
AsciiString fileStr = filePath;
const char *s = filePath.reverseFind('/');
if (s)
fileStr = s+1;
AsciiString fileName = "patches\\";
fileName.concat(fileStr);
DEBUG_LOG(("download URL split: %d [%s] [%s] [%s] [%s] [%s] [%s]\n",
success, connectionType.str(), server.str(), user.str(), pass.str(),
filePath.str(), fileName.str()));
if (!success)
return;
q.file = filePath;
q.localFile = fileName;
q.password = pass;
q.regKey = "";
q.server = server;
q.tryResume = TRUE;
q.userName = user;
std::list::iterator it = queuedDownloads.begin();
while (it != queuedDownloads.end())
{
if (it->localFile == q.localFile)
return; // don't add it if it exists already (because we can check multiple times)
++it;
}
queuedDownloads.push_back(q);
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool motdCallback( GHTTPRequest request, GHTTPResult result,
char * buffer, int bufferLen, void * param )
{
Int run = (Int)param;
if (run != timeThroughOnline)
{
DEBUG_CRASH(("Old callback being called!"));
return GHTTPTrue;
}
if (MOTDBuffer)
{
delete[] MOTDBuffer;
MOTDBuffer = NULL;
}
MOTDBuffer = NEW char[bufferLen];
memcpy(MOTDBuffer, buffer, bufferLen);
MOTDBuffer[bufferLen-1] = 0;
--checksLeftBeforeOnline;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
if (onlineCancelWindow && !checksLeftBeforeOnline)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
DEBUG_LOG(("------- Got MOTD before going online -------\n"));
DEBUG_LOG(("%s\n", (MOTDBuffer)?MOTDBuffer:""));
DEBUG_LOG(("--------------------------------------------\n"));
if (!checksLeftBeforeOnline)
startOnline();
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool configCallback( GHTTPRequest request, GHTTPResult result,
char * buffer, int bufferLen, void * param )
{
Int run = (Int)param;
if (run != timeThroughOnline)
{
DEBUG_CRASH(("Old callback being called!"));
return GHTTPTrue;
}
if (configBuffer)
{
delete[] configBuffer;
configBuffer = NULL;
}
if (result != GHTTPSuccess || bufferLen < 100)
{
if (!checkingForPatchBeforeGameSpy)
return GHTTPTrue;
--checksLeftBeforeOnline;
if (onlineCancelWindow && !checksLeftBeforeOnline)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
cantConnectBeforeOnline = TRUE;
if (!checksLeftBeforeOnline)
{
startOnline();
}
return GHTTPTrue;
}
configBuffer = NEW char[bufferLen];
memcpy(configBuffer, buffer, bufferLen);
configBuffer[bufferLen-1] = 0;
AsciiString fname;
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
FILE *fp = fopen(fname.str(), "wb");
if (fp)
{
fwrite(configBuffer, bufferLen, 1, fp);
fclose(fp);
}
--checksLeftBeforeOnline;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
if (onlineCancelWindow && !checksLeftBeforeOnline)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
DEBUG_LOG(("Got Config before going online\n"));
if (!checksLeftBeforeOnline)
startOnline();
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool configHeadCallback( GHTTPRequest request, GHTTPResult result,
char * buffer, int bufferLen, void * param )
{
Int run = (Int)param;
if (run != timeThroughOnline)
{
DEBUG_CRASH(("Old callback being called!"));
return GHTTPTrue;
}
DEBUG_LOG(("HTTP head resp: res=%d, len=%d, buf=[%s]\n", result, bufferLen, buffer));
if (result == GHTTPSuccess)
{
DEBUG_LOG(("Headers are [%s]\n", ghttpGetHeaders( request )));
AsciiString headers(ghttpGetHeaders( request ));
AsciiString line;
while (headers.nextToken(&line, "\n\r"))
{
AsciiString key, val;
line.nextToken(&key, ": ");
line.nextToken(&val, ": \r\n");
if (key.compare("Content-Length") == 0 && val.isNotEmpty())
{
Int serverLen = atoi(val.str());
Int fileLen = 0;
AsciiString fname;
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
FILE *fp = fopen(fname.str(), "rb");
if (fp)
{
fseek(fp, 0, SEEK_END);
fileLen = ftell(fp);
fclose(fp);
}
if (serverLen == fileLen)
{
// we don't need to download the MOTD again
--checksLeftBeforeOnline;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
if (onlineCancelWindow && !checksLeftBeforeOnline)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
if (configBuffer)
{
delete[] configBuffer;
configBuffer = NULL;
}
AsciiString fname;
fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
FILE *fp = fopen(fname.str(), "rb");
if (fp)
{
configBuffer = NEW char[fileLen];
fread(configBuffer, fileLen, 1, fp);
configBuffer[fileLen-1] = 0;
fclose(fp);
DEBUG_LOG(("Got Config before going online\n"));
if (!checksLeftBeforeOnline)
startOnline();
return GHTTPTrue;
}
}
}
}
}
// we need to download the MOTD again
std::string gameURL, mapURL;
std::string configURL, motdURL;
FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
ghttpGet( configURL.c_str(), GHTTPFalse, configCallback, param );
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool gamePatchCheckCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
{
Int run = (Int)param;
if (run != timeThroughOnline)
{
DEBUG_CRASH(("Old callback being called!"));
return GHTTPTrue;
}
--checksLeftBeforeOnline;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
DEBUG_LOG(("Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
if (result != GHTTPSuccess)
{
if (!checkingForPatchBeforeGameSpy)
return GHTTPTrue;
cantConnectBeforeOnline = TRUE;
if (!checksLeftBeforeOnline)
{
startOnline();
}
return GHTTPTrue;
}
AsciiString message = buffer;
AsciiString line;
while (message.nextToken(&line, "\r\n"))
{
AsciiString type, req, url;
Bool ok = TRUE;
ok &= line.nextToken(&type, " ");
ok &= line.nextToken(&req, " ");
ok &= line.nextToken(&url, " ");
if (ok && type == "patch")
{
DEBUG_LOG(("Saw a patch: %d/[%s]\n", atoi(req.str()), url.str()));
queuePatch( atoi(req.str()), url );
if (atoi(req.str()))
{
mustDownloadPatch = TRUE;
}
}
else if (ok && type == "server")
{
}
}
if (!checksLeftBeforeOnline)
{
startOnline();
}
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CancelPatchCheckCallbackAndReopenDropdown( void )
{
HandleCanceledDownload();
CancelPatchCheckCallback();
}
void CancelPatchCheckCallback( void )
{
s_asyncDNSLookupInProgress = FALSE;
HandleCanceledDownload(FALSE); // don't dropdown
checkingForPatchBeforeGameSpy = FALSE;
checksLeftBeforeOnline = 0;
if (onlineCancelWindow)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = NULL;
}
queuedDownloads.clear();
if (MOTDBuffer)
{
delete[] MOTDBuffer;
MOTDBuffer = NULL;
}
if (configBuffer)
{
delete[] configBuffer;
configBuffer = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
{
DEBUG_LOG(("overallStatsCallback() - Result=%d, len=%d\n", result, bufferLen));
if (result != GHTTPSuccess)
{
return GHTTPTrue;
}
OverallStats USA, China, GLA;
AsciiString message = buffer;
Int state = STATS_MAX; // STATS_MAX == none
AsciiString line;
OverallStats *stats = NULL;
while (message.nextToken(&line, "\n"))
{
line.trim();
line.toLower();
if (strstr(line.str(), "today"))
{
state = STATS_TODAY;
}
else if (strstr(line.str(), "yesterday"))
{
state = STATS_YESTERDAY;
}
else if (strstr(line.str(), "all time"))
{
state = STATS_ALLTIME;
}
else if (strstr(line.str(), "last week"))
{
state = STATS_LASTWEEK;
}
else if (state != STATS_MAX && strstr(line.str(), "usa"))
{
stats = &USA;
}
else if (state != STATS_MAX && strstr(line.str(), "china"))
{
stats = &China;
}
else if (state != STATS_MAX && strstr(line.str(), "gla"))
{
stats = &GLA;
}
if (stats)
{
AsciiString totalLine, winsLine, lossesLine;
message.nextToken(&totalLine, "\n");
message.nextToken(&winsLine, "\n");
message.nextToken(&lossesLine, "\n");
while (totalLine.isNotEmpty() && !isdigit(totalLine.getCharAt(0)))
{
totalLine = totalLine.str()+1;
}
while (winsLine.isNotEmpty() && !isdigit(winsLine.getCharAt(0)))
{
winsLine = winsLine.str()+1;
}
while (lossesLine.isNotEmpty() && !isdigit(lossesLine.getCharAt(0)))
{
lossesLine = lossesLine.str()+1;
}
if (totalLine.isNotEmpty() && winsLine.isNotEmpty() && lossesLine.isNotEmpty())
{
stats->wins[state] = atoi(winsLine.str());
stats->losses[state] = atoi(lossesLine.str());
}
stats = NULL;
}
}
HandleOverallStats(USA, China, GLA);
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
static GHTTPBool numPlayersOnlineCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
{
DEBUG_LOG(("numPlayersOnlineCallback() - Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
if (result != GHTTPSuccess)
{
return GHTTPTrue;
}
AsciiString message = buffer;
message.trim();
const char *s = message.reverseFind('\\');
if (!s)
{
return GHTTPTrue;
}
if (*s == '\\')
++s;
DEBUG_LOG(("Message was '%s', trimmed to '%s'=%d\n", buffer, s, atoi(s)));
HandleNumPlayersOnline(atoi(s));
return GHTTPTrue;
}
///////////////////////////////////////////////////////////////////////////////////////
void CheckOverallStats( void )
{
ghttpGet("http://gamestats.gamespy.com/ccgenerals/display.html",
GHTTPFalse, overallStatsCallback, NULL);
}
///////////////////////////////////////////////////////////////////////////////////////
void CheckNumPlayersOnline( void )
{
ghttpGet("http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals",
GHTTPFalse, numPlayersOnlineCallback, NULL);
}
///////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI asyncGethostbynameThreadFunc( void * szName )
{
HOSTENT *he = gethostbyname( (const char *)szName );
if (he)
{
s_asyncDNSThreadSucceeded = TRUE;
}
else
{
s_asyncDNSThreadSucceeded = FALSE;
}
s_asyncDNSThreadDone = TRUE;
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////
int asyncGethostbyname(char * szName)
{
static int stat = 0;
static unsigned long threadid;
if( stat == 0 )
{
/* Kick off gethostname thread */
s_asyncDNSThreadDone = FALSE;
s_asyncDNSThreadHandle = CreateThread( NULL, 0, asyncGethostbynameThreadFunc, szName, 0, &threadid );
if( s_asyncDNSThreadHandle == NULL )
{
return( LOOKUP_FAILED );
}
stat = 1;
}
if( stat == 1 )
{
if( s_asyncDNSThreadDone )
{
/* Thread finished */
stat = 0;
s_asyncDNSLookupInProgress = FALSE;
s_asyncDNSThreadHandle = NULL;
return( (s_asyncDNSThreadSucceeded)?LOOKUP_SUCCEEDED:LOOKUP_FAILED );
}
}
return( LOOKUP_INPROGRESS );
}
///////////////////////////////////////////////////////////////////////////////////////
// GameSpy's HTTP SDK has had at least 1 crash bug, so we're going to just bail and
// never try again if they crash us. We won't be able to get back online again (we'll
// time out) but at least we'll live.
static Bool isHttpOk = TRUE;
void HTTPThinkWrapper( void )
{
if (s_asyncDNSLookupInProgress)
{
Int ret = asyncGethostbyname("servserv.generals.ea.com");
switch(ret)
{
case LOOKUP_FAILED:
cantConnectBeforeOnline = TRUE;
startOnline();
break;
case LOOKUP_SUCCEEDED:
reallyStartPatchCheck();
break;
}
}
if (isHttpOk)
{
try
{
ghttpThink();
}
catch (...)
{
isHttpOk = FALSE; // we can't abort the login, since we might be done with the
// required checks and are fetching extras. If it is a required
// check, we'll time out normally.
}
}
}
///////////////////////////////////////////////////////////////////////////////////////
void StopAsyncDNSCheck( void )
{
if (s_asyncDNSThreadHandle)
{
#ifdef DEBUG_CRASHING
Int res =
#endif
TerminateThread(s_asyncDNSThreadHandle,0);
DEBUG_ASSERTCRASH(res, ("Could not terminate the Async DNS Lookup thread!")); // Thread still not killed!
}
s_asyncDNSThreadHandle = NULL;
s_asyncDNSLookupInProgress = FALSE;
}
///////////////////////////////////////////////////////////////////////////////////////
void StartPatchCheck( void )
{
checkingForPatchBeforeGameSpy = TRUE;
cantConnectBeforeOnline = FALSE;
timeThroughOnline++;
checksLeftBeforeOnline = 0;
onlineCancelWindow = MessageBoxCancel(TheGameText->fetch("GUI:CheckingForPatches"),
TheGameText->fetch("GUI:CheckingForPatches"), CancelPatchCheckCallbackAndReopenDropdown);
s_asyncDNSLookupInProgress = TRUE;
Int ret = asyncGethostbyname("servserv.generals.ea.com");
switch(ret)
{
case LOOKUP_FAILED:
cantConnectBeforeOnline = TRUE;
startOnline();
break;
case LOOKUP_SUCCEEDED:
reallyStartPatchCheck();
break;
}
}
///////////////////////////////////////////////////////////////////////////////////////
static void reallyStartPatchCheck( void )
{
checksLeftBeforeOnline = 4;
std::string gameURL, mapURL;
std::string configURL, motdURL;
FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
std::string proxy;
if (GetStringFromRegistry("", "Proxy", proxy))
{
if (!proxy.empty())
{
ghttpSetProxy(proxy.c_str());
}
}
// check for a patch first
DEBUG_LOG(("Game patch check: [%s]\n", gameURL.c_str()));
DEBUG_LOG(("Map patch check: [%s]\n", mapURL.c_str()));
DEBUG_LOG(("Config: [%s]\n", configURL.c_str()));
DEBUG_LOG(("MOTD: [%s]\n", motdURL.c_str()));
ghttpGet(gameURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
ghttpGet(mapURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
ghttpHead(configURL.c_str(), GHTTPFalse, configHeadCallback, (void *)timeThroughOnline);
ghttpGet(motdURL.c_str(), GHTTPFalse, motdCallback, (void *)timeThroughOnline);
// check total game stats
CheckOverallStats();
// check the users online
CheckNumPlayersOnline();
}
///////////////////////////////////////////////////////////////////////////////////////