Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 75 additions & 7 deletions Core/GameEngine/Source/GameNetwork/GameSpy/MainMenuUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include "GameClient/ShellHooks.h"

#include "gamespy/ghttp/ghttp.h"
// Must follow ghttp.h: gsavailable.h uses gsi_char without including gsplatform.h.
#include "gamespy/gsavailable.h"

#include "GameNetwork/DownloadManager.h"
#include "GameNetwork/GameSpy/BuddyThread.h"
Expand All @@ -59,6 +61,23 @@

static Bool checkingForPatchBeforeGameSpy = FALSE;
static Int checksLeftBeforeOnline = 0;

// Every GameSpy SDK entry point fails until this reaches GSIACAvailable.
static GSIACResult availableCheckResult = GSIACWaiting;
// Tracked separately from the result so a cancelled check cannot decrement
// checksLeftBeforeOnline after CancelPatchCheckCallback() has reset it.
static Bool availableCheckInProgress = FALSE;

static const char *getGameSpyGameName()
{
#if RTS_GENERALS
return "ccgenerals";
#elif RTS_ZEROHOUR
return "ccgenzh";
#else
#error "No GameSpy gamename defined for this build target"
#endif
}
Comment thread
sokie marked this conversation as resolved.
static Int timeThroughOnline = 0; // used to avoid having old callbacks cause problems
static Bool mustDownloadPatch = FALSE;
static Bool cantConnectBeforeOnline = FALSE;
Expand Down Expand Up @@ -140,6 +159,13 @@ static void noPatchBeforeOnlineCallback()
}
}

// noPatchBeforeOnlineCallback() cannot be reused here: it calls startOnline(), which
// would fail the same check and reopen this box.
static void backendUnavailableCallback()
{
HandleCanceledDownload();
}

///////////////////////////////////////////////////////////////////////////////////////

static Bool hasWriteAccess()
Expand Down Expand Up @@ -216,6 +242,15 @@ static void startOnline()

TheScriptEngine->signalUIInteract(TheShellHookNames[SHELL_SCRIPT_HOOK_MAIN_MENU_ONLINE_SELECTED]);

if (availableCheckResult != GSIACAvailable)
{
// Backend reported the title disabled; the GameSpy threads would all fail to start.
MessageBoxOk(TheGameText->fetch("GUI:GSErrorTitle"),
TheGameText->fetch("GUI:GSDisconReason4"),
backendUnavailableCallback);
return;
Comment thread
sokie marked this conversation as resolved.
}

DEBUG_ASSERTCRASH( !TheGameSpyBuddyMessageQueue, ("TheGameSpyBuddyMessageQueue exists!") );
DEBUG_ASSERTCRASH( !TheGameSpyPeerMessageQueue, ("TheGameSpyPeerMessageQueue exists!") );
DEBUG_ASSERTCRASH( !TheGameSpyInfo, ("TheGameSpyInfo exists!") );
Expand Down Expand Up @@ -555,6 +590,11 @@ void CancelPatchCheckCallbackAndReopenDropdown()
void CancelPatchCheckCallback()
{
s_asyncDNSLookupInProgress = FALSE;
if (availableCheckInProgress)
{
GSICancelAvailableCheck();
availableCheckInProgress = FALSE;
}
HandleCanceledDownload(FALSE); // don't dropdown
checkingForPatchBeforeGameSpy = FALSE;
checksLeftBeforeOnline = 0;
Expand Down Expand Up @@ -730,7 +770,7 @@ DWORD WINAPI asyncGethostbynameThreadFunc( void * szName )

///////////////////////////////////////////////////////////////////////////////////////

int asyncGethostbyname(char * szName)
int asyncGethostbyname(const char * szName)
{
static int stat = 0;
static unsigned long threadid;
Expand All @@ -739,7 +779,8 @@ int asyncGethostbyname(char * szName)
{
/* Kick off gethostname thread */
s_asyncDNSThreadDone = FALSE;
s_asyncDNSThreadHandle = CreateThread( nullptr, 0, asyncGethostbynameThreadFunc, szName, 0, &threadid );
s_asyncDNSThreadHandle = CreateThread( nullptr, 0, asyncGethostbynameThreadFunc,
const_cast<char *>(szName), 0, &threadid );

if( s_asyncDNSThreadHandle == nullptr )
{
Expand Down Expand Up @@ -773,8 +814,7 @@ void HTTPThinkWrapper()
{
if (s_asyncDNSLookupInProgress)
{
Char hostname[] = "servserv.generals.ea.com";
Int ret = asyncGethostbyname(hostname);
Int ret = asyncGethostbyname("servserv.generals.ea.com");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what the game uses to lookup the gamespy server?

If so it would probably be better to make it configurable.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my library and most other patches redirect DNS anyway, but for long term I agree all gamespy DNS records should be configurable so game can be pointed to other services.
I think that is out of scope for this PR.

switch(ret)
{
case LOOKUP_FAILED:
Expand All @@ -787,6 +827,30 @@ void HTTPThinkWrapper()
}
}

// GSIAvailableCheckThink() is what advances the check; it has to be called until it
// stops returning GSIACWaiting. Completing counts as one of checksLeftBeforeOnline so
// startOnline() waits for it, the same way it waits for the HTTP fetches.
if (availableCheckInProgress)
{
availableCheckResult = GSIAvailableCheckThink();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand how this here works. I am unable to review this logic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xezon replied below!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and some comments I added as I was debugging this and left in, apologies.
Went through all comments and cleaned them up or tidied them up.

if (availableCheckResult != GSIACWaiting)
{
availableCheckInProgress = FALSE;
--checksLeftBeforeOnline;
DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
Comment thread
sokie marked this conversation as resolved.
if (onlineCancelWindow && checksLeftBeforeOnline == 0)
{
TheWindowManager->winDestroy(onlineCancelWindow);
onlineCancelWindow = nullptr;
}

DEBUG_LOG(("Availability check returned %d", availableCheckResult));

if (checksLeftBeforeOnline == 0)
startOnline();
}
}

if (isHttpOk)
{
try
Expand Down Expand Up @@ -829,8 +893,7 @@ void StartPatchCheck()
TheGameText->fetch("GUI:CheckingForPatches"), CancelPatchCheckCallbackAndReopenDropdown);

s_asyncDNSLookupInProgress = TRUE;
Char hostname[] = "servserv.generals.ea.com";
Int ret = asyncGethostbyname(hostname);
Int ret = asyncGethostbyname("servserv.generals.ea.com");
switch(ret)
{
case LOOKUP_FAILED:
Expand All @@ -847,7 +910,12 @@ void StartPatchCheck()

static void reallyStartPatchCheck()
{
checksLeftBeforeOnline = 4;
checksLeftBeforeOnline = 5; // the four ghttp calls below, plus the availability check

GSICancelAvailableCheck(); // going online is retryable; do not leak the old socket
availableCheckResult = GSIACWaiting;
availableCheckInProgress = TRUE;
GSIStartAvailableCheck(getGameSpyGameName());

std::string gameURL, mapURL;
std::string configURL, motdURL;
Expand Down
12 changes: 12 additions & 0 deletions Core/GameEngine/Source/GameNetwork/GameSpy/Thread/PeerThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,18 @@ void PeerThreadClass::Thread_Function()

peer = peerInitialize( &callbacks );
DEBUG_ASSERTCRASH( peer != nullptr, ("null peer!") );
if (peer == nullptr)
{
// The assert above is a no-op in release, where the null goes on to fault in
// peerSetRoomWatchKeys(). startThread() reuses this object, so the flags have
// to be cleared or they leak into the next attempt.
markAsDisconnected();
PeerResponse resp;
resp.peerResponseType = PeerResponse::PEERRESPONSE_DISCONNECT;
resp.discon.reason = DISCONNECT_COULDNOTCONNECT;
TheGameSpyPeerMessageQueue->addResponse(resp);
Comment thread
sokie marked this conversation as resolved.
return;
}
m_isConnected = m_isConnecting = false;

qr2_register_key(EXECRC_KEY, EXECRC_STR);
Expand Down
Loading