/* ** 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: GameSpy.cpp ////////////////////////////////////////////////////// // GameSpy callbacks, etc // Author: Matthew D. Campbell, February 2002 #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "GameSpy/GP/GP.h" #include "GameSpy/gstats/gpersist.h" #include "GameNetwork/FirewallHelper.h" #include "GameNetwork/GameSpy.h" #include "GameNetwork/GameSpyChat.h" #include "GameNetwork/GameSpyGameInfo.h" #include "GameNetwork/GameSpyOverlay.h" #include "GameNetwork/GameSpyGP.h" #include "GameNetwork/GameSpyPersistentStorage.h" #include "GameNetwork/GameSpyThread.h" #include "GameNetwork/NAT.h" #include "GameClient/Shell.h" #include "GameClient/MessageBox.h" #include "GameClient/GameText.h" #include "GameClient/MapUtil.h" #include "Common/Version.h" #include "Common/MultiplayerSettings.h" #include "Common/PlayerTemplate.h" #include "Common/RandomValue.h" #include "Common/GlobalData.h" #include "Common/UserPreferences.h" #include "GameLogic/ScriptEngine.h" MutexClass TheGameSpyMutex; static UnsignedInt mainThreadID = 0; static Bool inThread = false; #define ISMAINTHREAD ( ThreadClass::_Get_Current_Thread_ID() == mainThreadID ) GameSpyThreadClass *TheGameSpyThread = NULL; /// polling/update thread function void GameSpyThreadClass::Thread_Function() { //poll network and update GameSpy object while (running) { inThread = true; if (m_doLogin) { m_doLogin = false; TheGameSpyChat->login(m_nick, m_pass, m_email); } if (m_readStats) { m_readStats = false; TheGameSpyPlayerInfo->threadReadFromServer(); } if (m_updateLocale) { m_updateLocale = false; TheGameSpyPlayerInfo->threadSetLocale(m_locale); } if (m_updateWins) { m_updateWins = false; TheGameSpyPlayerInfo->threadSetLocale(m_wins); } if (m_updateLosses) { m_updateLosses = false; TheGameSpyPlayerInfo->threadSetLocale(m_losses); } inThread = false; Switch_Thread(); } } AsciiString GameSpyThreadClass::getNextShellScreen( void ) { MutexClass::LockClass m(TheGameSpyMutex, 0); if (m.Failed()) return AsciiString::TheEmptyString; return m_nextShellScreen; } Bool GameSpyThreadClass::showLocaleSelect( void ) { MutexClass::LockClass m(TheGameSpyMutex, 0); if (m.Failed()) return false; return m_showLocaleSelect; } void GameSpyThreadClass::setNextShellScreen( AsciiString nextShellScreen ) { MutexClass::LockClass m(TheGameSpyMutex); m_nextShellScreen = nextShellScreen; } void GameSpyThreadClass::setShowLocaleSelect( Bool val ) { MutexClass::LockClass m(TheGameSpyMutex); m_showLocaleSelect = val; } void createRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param); class GameSpyChat : public GameSpyChatInterface { public: GameSpyChat(); virtual ~GameSpyChat(); virtual void init( void ); virtual void reset( void ); virtual void update( void ); virtual Bool isConnected( void ); virtual void login(AsciiString loginName, AsciiString password = AsciiString::TheEmptyString, AsciiString email = AsciiString::TheEmptyString); virtual void reconnectProfile( void ); virtual void disconnectFromChat( void ); virtual void UTMRoom( RoomType roomType, const char *key, const char *val, Bool authenticate = FALSE ); virtual void UTMPlayer( const char *name, const char *key, const char *val, Bool authenticate = FALSE ); virtual void startGame( void ); virtual void leaveRoom( RoomType roomType ); virtual void setReady( Bool ready ); virtual void enumPlayers( RoomType roomType, peerEnumPlayersCallback callback, void *userData ); virtual void startListingGames( peerListingGamesCallback callback ); virtual void stopListingGames( void ); virtual void joinGroupRoom( Int ID ); virtual void joinStagingRoom( GServer server, AsciiString password ); virtual void createStagingRoom( AsciiString gameName, AsciiString password, Int maxPlayers ); virtual void joinBestGroupRoom( void ); void loginQuick(AsciiString loginName); void loginProfile(AsciiString loginName, AsciiString password, AsciiString email); void setProfileID( Int ID ) { m_profileID = ID; } void _connectCallback(PEER peer, PEERBool success, void * param); void _nickErrorCallback(PEER peer, int type, const char * nick, void * param); void _GPConnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param); void _GPReconnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param); inline void finishJoiningGroupRoom( void ) { m_joiningGroupRoom = false; } inline void finishJoiningStagingRoom( void ) { m_joiningStagingRoom = false; } private: UnsignedInt m_loginTimeoutPeriod; // in ms UnsignedInt m_loginTimeout; Bool m_joiningGroupRoom; Bool m_joiningStagingRoom; GameSpyThreadClass thread; }; GameSpyChatInterface *TheGameSpyChat; GameSpyChatInterface *createGameSpyChat( void ) { mainThreadID = ThreadClass::_Get_Current_Thread_ID(); return NEW GameSpyChat; } void GameSpyChatInterface::clearGroupRoomList(void) { m_groupRooms.clear(); } GameSpyChat::GameSpyChat() { m_peer = NULL; } GameSpyChat::~GameSpyChat() { reset(); if (thread.Is_Running()) thread.Stop(); TheGameSpyThread = NULL; } void GameSpyChat::init( void ) { reset(); DEBUG_ASSERTCRASH(!thread.Is_Running(), ("Thread is running")); thread.Execute(); thread.Set_Priority(0); TheGameSpyThread = &thread; } void GameSpyChat::reset( void ) { MutexClass::LockClass m(TheGameSpyMutex); m_loginName.clear(); m_password.clear(); m_email.clear(); m_usingProfiles = false; m_profileID = 0; if (m_peer) peerShutdown(m_peer); m_peer = NULL; m_groupRooms.clear(); m_currentGroupRoomID = 0; m_loginTimeoutPeriod = 5000; m_loginTimeout = 0; m_joiningGroupRoom = false; m_joiningStagingRoom = false; //if (thread.Is_Running()) //thread.Stop(); } void GameSpyChat::update( void ) { MutexClass::LockClass m(TheGameSpyMutex, 0); if (!m.Failed() && m_peer) { if (!TheGameSpyThread->getNextShellScreen().isEmpty()) { TheShell->pop(); TheShell->push(TheGameSpyThread->getNextShellScreen()); TheGameSpyThread->setNextShellScreen( AsciiString.TheEmptyString ); } if (TheGameSpyThread->showLocaleSelect()) { TheGameSpyThread->setShowLocaleSelect(false); WindowLayout *layout = NULL; layout = TheWindowManager->winCreateLayout( AsciiString( "Menus/PopupLocaleSelect.wnd" ) ); layout->runInit(); layout->hide( FALSE ); layout->bringForward(); TheWindowManager->winSetModal( layout->getFirstWindow() ); } //if (!isConnected()) //return; peerThink(m_peer); gpProcess(TheGPConnection); if (TheNAT != NULL) { NATStateType NATState = TheNAT->update(); if (NATState == NATSTATE_DONE) { GameSpyLaunchGame(); } else if (NATState == NATSTATE_FAILED) { GameSpyLaunchGame(); } } if (TheFirewallHelper != NULL) { if (TheFirewallHelper->behaviorDetectionUpdate()) { TheGlobalData->m_firewallBehavior = TheFirewallHelper->getFirewallBehavior(); OptionPreferences *pref = NEW OptionPreferences; char num[16]; num[0] = 0; itoa(TheGlobalData->m_firewallBehavior, num, 10); AsciiString numstr; numstr = num; (*pref)["FirewallBehavior"] = numstr; pref->write(); // we are now done with the firewall helper delete TheFirewallHelper; TheFirewallHelper = NULL; } } UnsignedInt now = timeGetTime(); if (m_loginTimeout && now > m_loginTimeout) { // login timed out m_loginTimeout = 0; GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"Timed out connecting"), NULL); // Enable controls again //EnableLoginControls(TRUE); if (TheGameSpyGame) delete TheGameSpyGame; TheGameSpyGame = NULL; if (m_peer) peerShutdown(m_peer); m_peer = NULL; gpDisconnect(TheGPConnection); gpDestroy(TheGPConnection); } } } Bool GameSpyChat::isConnected( void ) { return m_peer && peerIsConnected(m_peer); } void GameSpyChat::UTMRoom( RoomType roomType, const char *key, const char *val, Bool authenticate ) { peerUTMRoom( m_peer, roomType, key, val, (authenticate)?PEERTrue:PEERFalse ); } void GameSpyChat::UTMPlayer( const char *name, const char *key, const char *val, Bool authenticate ) { peerUTMPlayer( m_peer, name, key, val, (authenticate)?PEERTrue:PEERFalse ); } void GameSpyChat::startGame( void ) { peerStartGame( m_peer, NULL, PEER_STOP_REPORTING ); } void GameSpyChat::leaveRoom( RoomType roomType ) { peerLeaveRoom( m_peer, roomType, NULL ); } void GameSpyChat::setReady( Bool ready ) { peerSetReady( m_peer, (ready)?PEERTrue:PEERFalse ); } void GameSpyChat::enumPlayers( RoomType roomType, peerEnumPlayersCallback callback, void *userData ) { peerEnumPlayers( m_peer, roomType, callback, userData ); } void GameSpyChat::startListingGames( peerListingGamesCallback callback ) { peerSetUpdatesRoomChannel( m_peer, "#gmtest_updates" ); peerStartListingGames( m_peer, NULL, callback, NULL ); } void GameSpyChat::stopListingGames( void ) { peerStopListingGames( m_peer ); } void GameSpyChat::joinGroupRoom( Int ID ) { if (m_joiningGroupRoom || m_joiningStagingRoom) return; m_joiningGroupRoom = true; if (getCurrentGroupRoomID()) { leaveRoom(GroupRoom); setCurrentGroupRoomID(0); } peerJoinGroupRoom(m_peer, ID, JoinRoomCallback, (void *)ID, PEERFalse); } void GameSpyChat::joinStagingRoom( GServer server, AsciiString password ) { if (m_joiningGroupRoom || m_joiningStagingRoom) return; m_joiningStagingRoom = true; peerJoinStagingRoom(m_peer, server, password.str(), JoinRoomCallback, server, PEERFalse); } void GameSpyChat::createStagingRoom( AsciiString gameName, AsciiString password, Int maxPlayers ) { if (m_joiningGroupRoom || m_joiningStagingRoom) return; m_joiningStagingRoom = true; Int oldGroupID = TheGameSpyChat->getCurrentGroupRoomID(); TheGameSpyChat->leaveRoom(GroupRoom); TheGameSpyChat->setCurrentGroupRoomID(0); DEBUG_LOG(("GameSpyChat::createStagingRoom - creating staging room, name is %s\n", m_loginName.str())); peerCreateStagingRoom(m_peer, m_loginName.str(), maxPlayers, password.str(), createRoomCallback, (void *)oldGroupID, PEERFalse); } void GameSpyChat::joinBestGroupRoom( void ) { if (m_groupRooms.size()) { int minID = -1; int minPlayers = 1000; GroupRoomMap::iterator iter = m_groupRooms.begin(); while (iter != m_groupRooms.end()) { GameSpyGroupRoom room = iter->second; DEBUG_LOG(("Group room %d: %s (%d, %d, %d, %d)\n", room.m_groupID, room.m_name.str(), room.m_numWaiting, room.m_maxWaiting, room.m_numGames, room.m_numPlaying)); if (minPlayers > 25 && room.m_numWaiting < minPlayers) { minID = room.m_groupID; minPlayers = room.m_numWaiting; } ++iter; } if (minID > 0) { joinGroupRoom(minID); } else { GSMessageBoxOk(UnicodeString(L"Oops"), UnicodeString(L"No empty group rooms"), NULL); } } else { GSMessageBoxOk(UnicodeString(L"Oops"), UnicodeString(L"No group rooms"), NULL); } } void GameSpyChat::disconnectFromChat( void ) { TheScriptEngine->signalUIInteract("GameSpyLogout"); if (m_peer) { peerShutdown(m_peer); m_peer = NULL; } if (TheGameSpyGame) delete TheGameSpyGame; TheGameSpyGame = NULL; } //----------------------------------------------------------------------- void DisconnectedCallback(PEER peer, const char * reason, void * param) { TheScriptEngine->signalUIInteract("GameSpyLogout"); if (TheGameSpyGame) delete TheGameSpyGame; TheGameSpyGame = NULL; GSMessageBoxOk(TheGameText->fetch("GUI:GSErrorTitle"), TheGameText->fetch("GUI:GSDisconnected"), NULL); WindowLayout * mainMenu = TheShell->findScreenByFilename("Menus/MainMenu.wnd"); if (mainMenu) { while (TheShell->top() != mainMenu) { TheShell->pop(); } } } void ReadyChangedCallback(PEER peer, const char * nick, PEERBool ready, void * param) { if (TheGameSpyGame) { Int slotNum = TheGameSpyGame->getSlotNum(nick); if (slotNum >= 0) { GameSlot *slot = TheGameSpyGame->getSlot(slotNum); if (slot) { if (ready) { slot->setAccept(); } else { slot->unAccept(); } if (TheGameSpyGame->amIHost()) { peerUTMRoom(TheGameSpyChat->getPeer(), StagingRoom, "SL/", GameInfoToAsciiString(TheGameSpyGame).str(), PEERFalse); } WOLDisplaySlotList(); } } } } void GameStartedCallback(PEER peer, unsigned int IP, const char * message, void * param) { GameSpyStartGame(); } void populateLobbyPlayerListbox(void); void PlayerJoinedCallback(PEER peer, RoomType roomType, const char * nick, void * param) { if (roomType == GroupRoom && TheGameSpyChat->getCurrentGroupRoomID()) { populateLobbyPlayerListbox(); } if (roomType == StagingRoom && TheGameSpyGame) { DEBUG_LOG(("StagingRoom, Game OK\n")); for (Int i=1; igetSlot(i); if (slot && slot->getState() == SLOT_OPEN) { DEBUG_LOG(("Putting %s in slot %d\n", nick, i)); UnicodeString uName; uName.translate(nick); slot->setState(SLOT_PLAYER, uName); slot->setColor(-1); slot->setPlayerTemplate(-1); slot->setStartPos(-1); slot->setTeamNumber(-1); TheGameSpyGame->resetAccepted(); Int id; UnsignedInt ip; PEERBool success = peerGetPlayerInfoNoWait(TheGameSpyChat->getPeer(), nick, &ip, &id); DEBUG_LOG(("PlayerJoinedCallback - result from peerGetPlayerInfoNoWait, %d: ip=%d.%d.%d.%d, id=%d\n", success, (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff, id)); success = PEERFalse; // silence compiler warnings in release build slot->setIP(ip); if (TheGameSpyGame->amIHost()) { peerUTMRoom(TheGameSpyChat->getPeer(), StagingRoom, "SL/", GameInfoToAsciiString(TheGameSpyGame).str(), PEERFalse); } WOLDisplaySlotList(); break; } } if (i == MAX_SLOTS) { // we got all the way through without room for him - kick him if (TheGameSpyGame->amIHost()) { peerUTMRoom(TheGameSpyChat->getPeer(), StagingRoom, "SL/", GameInfoToAsciiString(TheGameSpyGame).str(), PEERFalse); } } } } void PlayerLeftCallback(PEER peer, RoomType roomType, const char * nick, const char * reason, void * param) { if (roomType == GroupRoom && TheGameSpyChat->getCurrentGroupRoomID()) { populateLobbyPlayerListbox(); } if (roomType == StagingRoom && TheGameSpyGame) { Int slotNum = TheGameSpyGame->getSlotNum(nick); if (slotNum >= 0) { GameSlot *slot = TheGameSpyGame->getSlot(slotNum); if (slot) { slot->setState(SLOT_OPEN); DEBUG_LOG(("Removing %s from slot %d\n", nick, slotNum)); if (TheGameSpyGame->amIHost()) { peerUTMRoom(TheGameSpyChat->getPeer(), StagingRoom, "SL/", GameInfoToAsciiString(TheGameSpyGame).str(), PEERFalse); } TheGameSpyGame->resetAccepted(); WOLDisplaySlotList(); } } } } void PlayerChangedNickCallback(PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param) { if (TheGameSpyChat->getCurrentGroupRoomID()) { populateLobbyPlayerListbox(); } } void PingCallback(PEER peer, const char * nick, int ping, void * param) { DEBUG_LOG(("PingCallback\n")); } void CrossPingCallback(PEER peer, const char * nick1, const char * nick2, int crossPing, void * param) { DEBUG_LOG(("CrossPingCallback\n")); } static void RoomUTMCallback(PEER peer, RoomType roomType, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param) { DEBUG_LOG(("RoomUTMCallback: %s says %s = [%s]\n", nick, command, parameters)); if (roomType == StagingRoom && TheGameSpyGame) { Int slotNum = TheGameSpyGame->getSlotNum(nick); if (slotNum == 0 && !TheGameSpyGame->amIHost()) { if (!strcmp(command, "SL")) { AsciiString options = parameters; options.trim(); ParseAsciiStringToGameInfo(TheGameSpyGame, options.str()); WOLDisplaySlotList(); } else if (!strcmp(command, "HWS")) // HostWantsStart { Int slotNum = TheGameSpyGame->getLocalSlotNum(); GameSlot *slot = TheGameSpyGame->getSlot(slotNum); if (slot->isAccepted() == false) { GameSpyAddText(TheGameText->fetch("GUI:HostWantsToStart"), GSCOLOR_DEFAULT); } } } } } static void PlayerUTMCallback(PEER peer, const char * nick, const char * command, const char * parameters, PEERBool authenticated, void * param) { DEBUG_LOG(("PlayerUTMCallback: %s says %s = [%s]\n", nick, command, parameters)); if (TheGameSpyGame) { Int slotNum = TheGameSpyGame->getSlotNum(nick); if (slotNum != 0 && TheGameSpyGame->amIHost()) { if (!strcmp(command, "REQ")) { AsciiString options = parameters; options.trim(); Bool change = false; Bool shouldUnaccept = false; AsciiString key; options.nextToken(&key, "="); Int val = atoi(options.str()+1); UnsignedInt uVal = atoi(options.str()+1); DEBUG_LOG(("GameOpt request: key=%s, val=%s from player %d\n", key.str(), options.str(), slotNum)); GameSlot *slot = TheGameSpyGame->getSlot(slotNum); if (!slot) return; if (key == "Color") { if (val >= -1 && val < TheMultiplayerSettings->getNumColors() && val != slot->getColor()) { Bool colorAvailable = TRUE; if(val != -1 ) { for(Int i=0; i getSlot(i); if(val == checkSlot->getColor() && slot != checkSlot) { colorAvailable = FALSE; break; } } } if(colorAvailable) slot->setColor(val); change = true; } else { DEBUG_LOG(("Rejecting invalid color %d\n", val)); } } else if (key == "PlayerTemplate") { if (val >= PLAYERTEMPLATE_MIN && val < ThePlayerTemplateStore->getPlayerTemplateCount() && val != slot->getPlayerTemplate()) { slot->setPlayerTemplate(val); change = true; shouldUnaccept = true; } else { DEBUG_LOG(("Rejecting invalid PlayerTemplate %d\n", val)); } } else if (key == "StartPos") { if (val >= -1 && val < MAX_SLOTS && val != slot->getStartPos()) { slot->setStartPos(val); change = true; shouldUnaccept = true; } else { DEBUG_LOG(("Rejecting invalid startPos %d\n", val)); } } else if (key == "Team") { if (val >= -1 && val < MAX_SLOTS/2 && val != slot->getTeamNumber()) { slot->setTeamNumber(val); change = true; shouldUnaccept = true; } else { DEBUG_LOG(("Rejecting invalid team %d\n", val)); } } else if (key == "IP") { if (uVal != slot->getIP()) { slot->setIP(uVal); change = true; shouldUnaccept = true; } else { DEBUG_LOG(("Rejecting invalid IP %d\n", uVal)); } } else if (key == "NAT") { if ((val >= FirewallHelperClass::FIREWALL_TYPE_SIMPLE) && (val <= FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA)) { slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)val); DEBUG_LOG(("Setting NAT behavior to %d for player %d\n", val, slotNum)); change = true; } else { DEBUG_LOG(("Rejecting invalid NAT behavior %d from player %d\n", val, slotNum)); } } if (change) { if (shouldUnaccept) TheGameSpyGame->resetAccepted(); peerUTMRoom(TheGameSpyChat->getPeer(), StagingRoom, "SL/", GameInfoToAsciiString(TheGameSpyGame).str(), PEERFalse); WOLDisplaySlotList(); DEBUG_LOG(("Slot value is color=%d, PlayerTemplate=%d, startPos=%d, team=%d, IP=0x%8.8X\n", slot->getColor(), slot->getPlayerTemplate(), slot->getStartPos(), slot->getTeamNumber(), slot->getIP())); DEBUG_LOG(("Slot list updated to %s\n", GameInfoToAsciiString(TheGameSpyGame).str())); } } } } } void GOABasicCallback(PEER peer, PEERBool playing, char * outbuf, int maxlen, void * param) { _snprintf(outbuf, maxlen, "\\gamename\\c&cgenerals\\gamever\\%s\\location\\%d", TheVersion->getAsciiVersion().str(), 0); outbuf[maxlen-1] = 0; DEBUG_LOG(("GOABasicCallback: [%s]\n", outbuf)); TheGameSpyGame->gotGOACall(); } void GOAInfoCallback(PEER peer, PEERBool playing, char * outbuf, int maxlen, void * param) { _snprintf(outbuf, maxlen, "\\hostname\\%s\\hostport\\%d\\mapname\\%s\\gametype\\%s\\numplayers\\%d\\maxplayers\\%d\\gamemode\\%s", TheGameSpyChat->getLoginName().str(), 0, TheGameSpyGame->getMap().str(), "battle", TheGameSpyGame->getNumPlayers(), TheGameSpyGame->getMaxPlayers(), "wait"); outbuf[maxlen-1] = 0; DEBUG_LOG(("GOAInfoCallback: [%s]\n", outbuf)); TheGameSpyGame->gotGOACall(); } void GOARulesCallback(PEER peer, PEERBool playing, char * outbuf, int maxlen, void * param) { _snprintf(outbuf, maxlen, "\\password\\0\\seed\\%d", TheGameSpyGame->getSeed()); outbuf[maxlen-1] = 0; DEBUG_LOG(("GOARulesCallback: [%s]\n", outbuf)); TheGameSpyGame->gotGOACall(); } void GOAPlayersCallback(PEER peer, PEERBool playing, char * outbuf, int maxlen, void * param) { AsciiString buf, tmp; for (Int i=0; igetSlot(i); AsciiString name; switch (slot->getState()) { case SLOT_OPEN: name = "O"; break; case SLOT_CLOSED: name = "X"; break; case SLOT_EASY_AI: name = "CE"; break; case SLOT_MED_AI: name = "CM"; break; case SLOT_BRUTAL_AI: name = "CB"; break; case SLOT_PLAYER: { AsciiString tmp; tmp.translate(slot->getName()); name.format("H%s", tmp.str()); } break; default: name = "?"; break; } tmp.format("\\player_%d\\%s\\color_%d\\%d\\faction_%d\\%d\\team_%d\\%d\\pos_%d\\%d", i, name.str(), i, slot->getColor(), i, slot->getPlayerTemplate(), i, slot->getTeamNumber(), i, slot->getStartPos()); buf.concat(tmp); } _snprintf(outbuf, maxlen, buf.str()); outbuf[maxlen-1] = 0; DEBUG_LOG(("GOAPlayersCallback: [%s]\n", outbuf)); TheGameSpyGame->gotGOACall(); } void JoinRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param) { DEBUG_LOG(("JoinRoomCallback: success==%d, result==%d\n", success, result)); switch (roomType) { case GroupRoom: { ((GameSpyChat *)TheGameSpyChat)->finishJoiningGroupRoom(); if (success) { // update our internal group room ID TheGameSpyChat->setCurrentGroupRoomID((Int)param); // see if we need to change screens WindowLayout *layout = TheShell->top(); AsciiString windowFile = layout->getFilename(); DEBUG_LOG(("Joined group room, active screen was %s\n", windowFile.str())); if (windowFile.compareNoCase("Menus/WOLCustomLobby.wnd") == 0) { // already on the right screen - just refresh it //GroupRoomJoinLobbyRefresh(); } else { // change to the right screen TheShell->pop(); TheShell->push("Menus/WOLCustomLobby.wnd"); } } else { // failed to join lobby - bail to welcome screen WindowLayout *layout = TheShell->top(); AsciiString windowFile = layout->getFilename(); DEBUG_LOG(("Joined group room, active screen was %s\n", windowFile.str())); if (windowFile.compareNoCase("Menus/WOLWelcomeMenu.wnd") != 0) { TheShell->pop(); TheShell->push("Menus/WOLWelcomeMenu.wnd"); } GSMessageBoxOk(UnicodeString(L"Oops"), UnicodeString(L"Unable to join group room"), NULL); } // Update buddy location if (TheGameSpyChat->getUsingProfile()) { if (TheGameSpyChat->getCurrentGroupRoomID()) { AsciiString s; s.format("c&cgenerals://0.0.0.0:0/%d", TheGameSpyChat->getCurrentGroupRoomID()); gpSetStatus(TheGPConnection, GP_CHATTING, "Chatting", s.str()); } else { gpSetStatus(TheGPConnection, GP_ONLINE, "Online", ""); } } } break; case StagingRoom: { ((GameSpyChat *)TheGameSpyChat)->finishJoiningStagingRoom(); if (success) { DEBUG_LOG(("JoinRoomCallback - Joined staging room\n")); GServer server = (GServer)param; // leave any chat channels TheGameSpyChat->leaveRoom(GroupRoom); TheGameSpyChat->setCurrentGroupRoomID(0); // set up game info TheGameSpyGame->enterGame(); TheGameSpyGame->setServer(server); GameSlot *slot = TheGameSpyGame->getSlot(0); AsciiString options, hostName; hostName = ServerGetPlayerStringValue(server, 0, "player", ""); UnicodeString uHostName; uHostName.translate(hostName.str() + 1); // go past the 'H' slot->setState(SLOT_PLAYER, uHostName); UnsignedInt localIP = peerGetLocalIP(TheGameSpyChat->getPeer()); GetLocalChatConnectionAddress("peerchat.gamespy.com", 6667, localIP); localIP = ntohl(localIP); // The IP returned from GetLocalChatConnectionAddress is in network byte order. options.format("IP=%d", localIP); peerUTMPlayer(TheGameSpyChat->getPeer(), hostName.str(), "REQ/", options.str(), PEERFalse); options.format("NAT=%d", TheFirewallHelper->getFirewallBehavior()); peerUTMPlayer(TheGameSpyChat->getPeer(), hostName.str(), "REQ/", options.str(), PEERFalse); // refresh the map cache TheMapCache->updateCache(); // switch screens TheShell->pop(); TheShell->push("Menus/GameSpyGameOptionsMenu.wnd"); } else { // let the user know GSMessageBoxOk(UnicodeString(L"Oops"), UnicodeString(L"Unable to join game"), NULL); DEBUG_LOG(("JoinRoomCallback - Failed to join staging room.\n")); } // Update buddy location if (TheGameSpyChat->getUsingProfile()) { if (TheGameSpyChat->getCurrentGroupRoomID()) { AsciiString s; s.format("c&cgenerals://0.0.0.0:0/%d", TheGameSpyChat->getCurrentGroupRoomID()); gpSetStatus(TheGPConnection, GP_CHATTING, "Chatting", s.str()); } else { gpSetStatus(TheGPConnection, GP_ONLINE, "Online", ""); } } break; } } //*didJoin = (success == PEERJoinSuccess || success == PEERTrue); } void createRoomCallback(PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void *param) { DEBUG_LOG(("CreateRoomCallback: success==%d, result==%d\n", success, result)); if (roomType != StagingRoom) { DEBUG_CRASH(("Non-staging room create!")); return; } ((GameSpyChat *)TheGameSpyChat)->finishJoiningStagingRoom(); Int oldGroupID = (Int)param; if (success) { // set up the game info UnsignedInt localIP = peerGetLocalIP(TheGameSpyChat->getPeer()); DEBUG_LOG(("createRoomCallback - peerGetLocalIP returned %d.%d.%d.%d as the local IP\n", localIP >> 24, (localIP >> 16) & 0xff, (localIP >> 8) & 0xff, localIP & 0xff)); // GetLocalChatConnectionAddress("peerchat.gamespy.com", 6667, localIP); // DEBUG_LOG(("createRoomCallback - GetLocalChatConnectionAddress returned %d.%d.%d.%d as the local IP\n", // localIP >> 24, (localIP >> 16) & 0xff, (localIP >> 8) & 0xff, localIP & 0xff)); localIP = ntohl(localIP); // The IP returned from GetLocalChatConnectionAddress is in network byte order. TheGameSpyGame->setLocalIP(localIP); UnicodeString name; name.translate(TheGameSpyChat->getLoginName()); TheGameSpyGame->enterGame(); // TheGameSpyGame->setSeed(GameClientRandomValue(0, INT_MAX - 1)); TheGameSpyGame->setSeed(GetTickCount()); GameSlot newSlot; newSlot.setState(SLOT_PLAYER, name); newSlot.setIP(localIP); TheGameSpyGame->setSlot(0,newSlot); TheMapCache->updateCache(); TheGameSpyGame->setMap(TheGlobalData->m_mapName); AsciiString asciiMap; asciiMap = TheGlobalData->m_mapName; asciiMap.toLower(); std::map::iterator it = TheMapCache->find(asciiMap); if (it != TheMapCache->end()) { TheGameSpyGame->getSlot(0)->setMapAvailability(true); TheGameSpyGame->setMapCRC( it->second.m_CRC ); TheGameSpyGame->setMapSize( it->second.m_filesize ); TheGameSpyGame->adjustSlotsForMap(); // BGC- adjust the slots for the new map. } // change to the proper screen TheShell->pop(); TheShell->push("Menus/GameSpyGameOptionsMenu.wnd"); } else { // join the lobby again TheGameSpyChat->joinGroupRoom(oldGroupID); GSMessageBoxOk(UnicodeString(L"Oops"), UnicodeString(L"Unable to create game"), NULL); } // Update buddy location if (TheGameSpyChat->getUsingProfile()) { if (TheGameSpyChat->getCurrentGroupRoomID()) { AsciiString s; s.format("c&cgenerals://0.0.0.0:0/%d", TheGameSpyChat->getCurrentGroupRoomID()); gpSetStatus(TheGPConnection, GP_CHATTING, "Chatting", s.str()); } else { gpSetStatus(TheGPConnection, GP_ONLINE, "Online", ""); } } } // Gets called once for each group room when listing group rooms. // After this has been called for each group room, it will be // called one more time with groupID==0 and name==NULL. ///////////////////////////////////////////////////////////////// void ListGroupRoomsCallback(PEER peer, PEERBool success, int groupID, GServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param) { DEBUG_LOG(("ListGroupRoomsCallback\n")); if (success) { if (groupID) { GroupRoomMap *grMap = TheGameSpyChat->getGroupRooms(); (*grMap)[groupID].m_name = name; (*grMap)[groupID].m_numWaiting = numWaiting; (*grMap)[groupID].m_maxWaiting = maxWaiting; (*grMap)[groupID].m_numGames = numGames; (*grMap)[groupID].m_numPlaying = numPlaying; (*grMap)[groupID].m_groupID = groupID; } else { // we've got the complete list. UpdateGroupRoomList(); } } } // Called when peerConnect completes. static void connectCallback(PEER peer, PEERBool success, void * param) { ((GameSpyChat *)TheGameSpyChat)->_connectCallback(peer, success, param); } static void nickErrorCallback(PEER peer, int type, const char * nick, void * param) { ((GameSpyChat *)TheGameSpyChat)->_nickErrorCallback(peer, type, nick, param); } static void GPConnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param) { ((GameSpyChat *)TheGameSpyChat)->_GPConnectCallback(pconnection, arg, param); } static void GPReconnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param) { ((GameSpyChat *)TheGameSpyChat)->_GPReconnectCallback(pconnection, arg, param); } void GameSpyChat::_connectCallback(PEER peer, PEERBool success, void * param) { m_loginTimeout = 0; if (!success) { GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"Failed to connect"), NULL); DEBUG_LOG(("GameSpyChat::_connectCallback - failed to connect.\n")); } if(!success) { peerShutdown(m_peer); m_peer = NULL; gpDisconnect(TheGPConnection); gpDestroy(TheGPConnection); // Enable controls again //EnableLoginControls(TRUE); return; } DEBUG_LOG(("Connected as profile %d (%s)\n", m_profileID, m_loginName.str())); TheGameSpyGame = NEW GameSpyGameInfo; // Enable controls again //EnableLoginControls(TRUE); // the readFromServer() call will set the screen if (m_profileID) { TheGameSpyPlayerInfo->readFromServer(); } TheScriptEngine->signalUIInteract("GameSpyLogin"); TheShell->popImmediate(); TheShell->push("Menus/WOLWelcomeMenu.wnd"); clearGroupRoomList(); peerListGroupRooms(m_peer, ListGroupRoomsCallback, NULL, PEERFalse); } // Called if there's an error with the nick. void GameSpyChat::_nickErrorCallback(PEER peer, int type, const char * nick, void * param) { // Let the user know. ///////////////////// if(type == PEER_IN_USE) { AsciiString nickStr = nick; AsciiString origName, appendedVal; nickStr.nextToken(&origName, "{}"); nickStr.nextToken(&appendedVal, "{}"); Int intVal = 1; if (!appendedVal.isEmpty()) { intVal = atoi(appendedVal.str()) + 1; } DEBUG_LOG(("Nickname taken: origName=%s, tries=%d\n", origName.str(), intVal)); if (intVal < 10) { nickStr.format("%s{%d}", origName.str(), intVal); // Retry the connect with a similar nick. DEBUG_LOG(("GameSpyChat::_nickErrorCallback - nick was taken, setting to %s\n", nickStr.str())); m_loginName = nickStr; peerRetryWithNick(peer, nickStr.str()); } else { GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"That nickname is already taken; please choose another one."), NULL); // Cancel the connect. peerRetryWithNick(peer, NULL); // Enable controls again //EnableLoginControls(TRUE); m_loginTimeout = 0; } } else { GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"That nickname contains at least 1 invalid character, please choose another one."), NULL); // Cancel the connect. peerRetryWithNick(peer, NULL); // Enable controls again //EnableLoginControls(TRUE); m_loginTimeout = 0; } } void GameSpyChat::_GPConnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param) { DEBUG_LOG(("GPConnectCallback:\n")); GPResult *res = (GPResult *)param; *res = arg->result; setProfileID(arg->profile); if (*res != GP_NO_ERROR) { // couldn't connect. bummer. GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"Error connecting to buddy server"), NULL); gpDisconnect(TheGPConnection); gpDestroy(TheGPConnection); m_loginTimeout = 0; return; } // Connect to chat. /////////////////// peerConnect(m_peer, m_loginName.str(), m_profileID, nickErrorCallback, connectCallback, NULL, PEERFalse); } static Bool inGPReconnect = false; void GameSpyChat::_GPReconnectCallback(GPConnection * pconnection, GPConnectResponseArg * arg, void * param) { inGPReconnect = false; DEBUG_LOG(("GPConnectCallback:\n")); setProfileID(arg->profile); if (arg->result != GP_NO_ERROR) { // couldn't connect. bummer. GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"Error connecting to buddy server"), NULL); gpDisconnect(TheGPConnection); gpDestroy(TheGPConnection); return; } else { // yay! we're back in! GSMessageBoxOk(UnicodeString(L"Connected!"), UnicodeString(L"Reonnected to buddy server"), NULL); } } void GameSpyChat::loginProfile(AsciiString loginName, AsciiString password, AsciiString email) { // Connect to GP. /////////////////// m_profileID = 0; gpInitialize(TheGPConnection, 0); gpSetCallback(TheGPConnection, GP_ERROR, (GPCallback)GPErrorCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_REQUEST, (GPCallback)GPRecvBuddyRequestCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_MESSAGE, (GPCallback)GPRecvBuddyMessageCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_STATUS, (GPCallback)GPRecvBuddyStatusCallback, NULL); GPResult res = GP_PARAMETER_ERROR; m_loginName = loginName; DEBUG_LOG(("GameSpyChat::loginProfile - m_loginName set to %s\n", m_loginName.str())); m_password = password; m_email = email; gpConnect(TheGPConnection, loginName.str(), email.str(), password.str(), GP_FIREWALL, GP_NON_BLOCKING, (GPCallback)GPConnectCallback, &res); /* if (res != GP_NO_ERROR) { // couldn't connect. bummer. GSMessageBoxOk(UnicodeString(L"Error connecting"), UnicodeString(L"Error connecting to buddy server"), NULL); gpDisconnect(TheGPConnection); gpDestroy(TheGPConnection); loginTimeout = 0; return; } // Connect to chat. /////////////////// GameSpyLocalNickname = loginName; peerConnect(TheGameSpyChat->getPeer(), loginName.str(), GameSpyLocalProfile, nickErrorCallback, connectCallback, NULL, PEERFalse); */ } void GameSpyChat::reconnectProfile( void ) { if (inGPReconnect) return; inGPReconnect = true; gpInitialize(TheGPConnection, 0); gpSetCallback(TheGPConnection, GP_ERROR, (GPCallback)GPErrorCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_REQUEST, (GPCallback)GPRecvBuddyRequestCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_MESSAGE, (GPCallback)GPRecvBuddyMessageCallback, NULL); gpSetCallback(TheGPConnection, GP_RECV_BUDDY_STATUS, (GPCallback)GPRecvBuddyStatusCallback, NULL); gpConnect(TheGPConnection, m_loginName.str(), m_email.str(), m_password.str(), GP_FIREWALL, GP_NON_BLOCKING, (GPCallback)GPReconnectCallback, NULL); } void GameSpyChat::loginQuick(AsciiString login) { m_profileID = 0; // no buddy stuff // Connect to chat. /////////////////// m_loginName = login; DEBUG_LOG(("GameSpyChat::loginQuick - setting login to %s\n", m_loginName)); peerConnect(m_peer, login.str(), 0, nickErrorCallback, connectCallback, NULL, PEERFalse); } void GameSpyChat::login(AsciiString loginName, AsciiString password, AsciiString email) { MutexClass::LockClass m(TheGameSpyMutex); if ( ISMAINTHREAD ) { thread.queueLogin(loginName, password, email); return; } // Setup the callbacks. /////////////////////// PEERCallbacks callbacks; memset(&callbacks, 0, sizeof(PEERCallbacks)); callbacks.disconnected = DisconnectedCallback; callbacks.readyChanged = ReadyChangedCallback; callbacks.roomMessage = RoomMessageCallback; callbacks.playerMessage = PlayerMessageCallback; callbacks.gameStarted = GameStartedCallback; callbacks.playerJoined = PlayerJoinedCallback; callbacks.playerLeft = PlayerLeftCallback; callbacks.playerChangedNick = PlayerChangedNickCallback; callbacks.ping = PingCallback; callbacks.crossPing = CrossPingCallback; callbacks.roomUTM = RoomUTMCallback; callbacks.playerUTM = PlayerUTMCallback; callbacks.GOABasic = GOABasicCallback; callbacks.GOAInfo = GOAInfoCallback; callbacks.GOARules = GOARulesCallback; callbacks.GOAPlayers = GOAPlayersCallback; //callbacks.globalKeyChanged = GlobalKeyChanged; callbacks.param = NULL; // Init peer. ///////////// m_peer = peerInitialize(&callbacks); if(!m_peer) { GSMessageBoxOk(UnicodeString(L"No Peer"), UnicodeString(L"No Peer"), NULL); return; } // Setup which rooms to do pings and cross-pings in. //////////////////////////////////////////////////// PEERBool pingRooms[NumRooms]; PEERBool crossPingRooms[NumRooms]; pingRooms[TitleRoom] = PEERTrue; pingRooms[GroupRoom] = PEERTrue; pingRooms[StagingRoom] = PEERFalse; crossPingRooms[TitleRoom] = PEERFalse; crossPingRooms[GroupRoom] = PEERFalse; crossPingRooms[StagingRoom] = PEERTrue; // Set the title. ///////////////// if(!peerSetTitle(m_peer, "gmtest", "HA6zkS", "gmtest", "HA6zkS", 15, pingRooms, crossPingRooms)) { GSMessageBoxOk(UnicodeString(L"Error setting title"), UnicodeString(L"Error setting title"), NULL); peerShutdown(m_peer); m_peer = NULL; return; } //EnableLoginControls( FALSE ); m_loginTimeout = timeGetTime() + m_loginTimeoutPeriod; if (!loginName.isEmpty() && !email.isEmpty() && !password.isEmpty()) { m_usingProfiles = true; loginProfile(loginName, password, email); } else { m_usingProfiles = false; loginQuick(loginName); } }