/* ** 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: PopupHostGame.cpp ///////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Electronic Arts Pacific. // // Confidential Information // Copyright (C) 2002 - All Rights Reserved // //----------------------------------------------------------------------------- // // created: Jul 2002 // // Filename: PopupHostGame.cpp // // author: Chris Huybregts // // purpose: Contains the Callbacks for the Host Game Popus // //----------------------------------------------------------------------------- /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // SYSTEM INCLUDES //////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // USER INCLUDES ////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GlobalData.h" #include "Common/NameKeyGenerator.h" #include "Common/Version.h" #include "GameClient/WindowLayout.h" #include "GameClient/Gadget.h" #include "GameClient/GameText.h" #include "GameClient/KeyDefs.h" #include "GameClient/GadgetTextEntry.h" #include "GameClient/GadgetCheckBox.h" #include "GameClient/GadgetComboBox.h" #include "GameClient/GadgetListBox.h" #include "GameNetwork/GameSpy/GSConfig.h" #include "GameNetwork/GameSpy/Peerdefs.h" #include "GameNetwork/GameSpy/PeerThread.h" #include "GameNetwork/GameSpyOverlay.h" #include "GameNetwork/GameSpy/LadderDefs.h" #include "Common/CustomMatchPreferences.h" #include "Common/LadderPreferences.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- static NameKeyType parentPopupID = NAMEKEY_INVALID; static NameKeyType textEntryGameNameID = NAMEKEY_INVALID; static NameKeyType buttonCreateGameID = NAMEKEY_INVALID; static NameKeyType checkBoxAllowObserversID = NAMEKEY_INVALID; static NameKeyType textEntryGameDescriptionID = NAMEKEY_INVALID; static NameKeyType buttonCancelID = NAMEKEY_INVALID; static NameKeyType textEntryLadderPasswordID = NAMEKEY_INVALID; static NameKeyType comboBoxLadderNameID = NAMEKEY_INVALID; static NameKeyType textEntryGamePasswordID = NAMEKEY_INVALID; static GameWindow *parentPopup = NULL; static GameWindow *textEntryGameName = NULL; static GameWindow *buttonCreateGame = NULL; static GameWindow *checkBoxAllowObservers = NULL; static GameWindow *textEntryGameDescription = NULL; static GameWindow *buttonCancel = NULL; static GameWindow *comboBoxLadderName = NULL; static GameWindow *textEntryLadderPassword = NULL; static GameWindow *textEntryGamePassword = NULL; #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif void createGame( void ); //----------------------------------------------------------------------------- // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // Ladders -------------------------------------------------------------------------------- static bool isPopulatingLadderBox = false; void CustomMatchHideHostPopup(Bool hide) { if (!parentPopup) return; parentPopup->winHide( hide ); } void HandleCustomLadderSelection(Int ladderID) { if (!parentPopup) return; CustomMatchPreferences pref; if (ladderID == 0) { pref.setLastLadder(AsciiString::TheEmptyString, 0); pref.write(); return; } const LadderInfo *info = TheLadderList->findLadderByIndex(ladderID); if (!info) { pref.setLastLadder(AsciiString::TheEmptyString, 0); } else { pref.setLastLadder(info->address, info->port); } pref.write(); } void PopulateCustomLadderListBox( GameWindow *win ) { if (!parentPopup || !win) return; isPopulatingLadderBox = true; CustomMatchPreferences pref; Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED]; Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED]; Color favoriteColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED]; Color localColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED]; Int index; GadgetListBoxReset( win ); std::set usedLadders; // start with "No Ladder" index = GadgetListBoxAddEntryText( win, TheGameText->fetch("GUI:NoLadder"), normalColor, -1 ); GadgetListBoxSetItemData( win, 0, index ); // add the last ladder Int selectedPos = 0; AsciiString lastLadderAddr = pref.getLastLadderAddr(); UnsignedShort lastLadderPort = pref.getLastLadderPort(); const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort ); if (info && info->index > 0 && info->validCustom) { usedLadders.insert(info); index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 ); GadgetListBoxSetItemData( win, (void *)(info->index), index ); selectedPos = index; } // our recent ladders LadderPreferences ladPref; ladPref.loadProfile( TheGameSpyInfo->getLocalProfileID() ); const LadderPrefMap recentLadders = ladPref.getRecentLadders(); for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit) { AsciiString addr = cit->second.address; UnsignedShort port = cit->second.port; if (addr == lastLadderAddr && port == lastLadderPort) continue; const LadderInfo *info = TheLadderList->findLadder( addr, port ); if (info && info->index > 0 && info->validCustom && usedLadders.find(info) == usedLadders.end()) { usedLadders.insert(info); index = GadgetListBoxAddEntryText( win, info->name, favoriteColor, -1 ); GadgetListBoxSetItemData( win, (void *)(info->index), index ); } } // local ladders const LadderInfoList *lil = TheLadderList->getLocalLadders(); LadderInfoList::const_iterator lit; for (lit = lil->begin(); lit != lil->end(); ++lit) { const LadderInfo *info = *lit; if (info && info->index < 0 && info->validCustom && usedLadders.find(info) == usedLadders.end()) { usedLadders.insert(info); index = GadgetListBoxAddEntryText( win, info->name, localColor, -1 ); GadgetListBoxSetItemData( win, (void *)(info->index), index ); } } // special ladders lil = TheLadderList->getSpecialLadders(); for (lit = lil->begin(); lit != lil->end(); ++lit) { const LadderInfo *info = *lit; if (info && info->index > 0 && info->validCustom && usedLadders.find(info) == usedLadders.end()) { usedLadders.insert(info); index = GadgetListBoxAddEntryText( win, info->name, specialColor, -1 ); GadgetListBoxSetItemData( win, (void *)(info->index), index ); } } // standard ladders lil = TheLadderList->getStandardLadders(); for (lit = lil->begin(); lit != lil->end(); ++lit) { const LadderInfo *info = *lit; if (info && info->index > 0 && info->validCustom && usedLadders.find(info) == usedLadders.end()) { usedLadders.insert(info); index = GadgetListBoxAddEntryText( win, info->name, normalColor, -1 ); GadgetListBoxSetItemData( win, (void *)(info->index), index ); } } GadgetListBoxSetSelected( win, selectedPos ); isPopulatingLadderBox = false; } void PopulateCustomLadderComboBox( void ) { if (!parentPopup || !comboBoxLadderName) return; isPopulatingLadderBox = true; CustomMatchPreferences pref; AsciiString userPrefFilename; Int localProfile = TheGameSpyInfo->getLocalProfileID(); userPrefFilename.format("GeneralsOnline\\CustomPref%d.ini", localProfile); pref.load(userPrefFilename); std::set usedLadders; Color specialColor = GameSpyColor[GSCOLOR_MAP_SELECTED]; Color normalColor = GameSpyColor[GSCOLOR_MAP_UNSELECTED]; Int index; GadgetComboBoxReset( comboBoxLadderName ); index = GadgetComboBoxAddEntry( comboBoxLadderName, TheGameText->fetch("GUI:NoLadder"), normalColor ); GadgetComboBoxSetItemData( comboBoxLadderName, index, 0 ); Int selectedPos = 0; AsciiString lastLadderAddr = pref.getLastLadderAddr(); UnsignedShort lastLadderPort = pref.getLastLadderPort(); const LadderInfo *info = TheLadderList->findLadder( lastLadderAddr, lastLadderPort ); if (info && info->validCustom) { usedLadders.insert(info); index = GadgetComboBoxAddEntry( comboBoxLadderName, info->name, specialColor ); GadgetComboBoxSetItemData( comboBoxLadderName, index, (void *)(info->index) ); selectedPos = index; } LadderPreferences ladPref; ladPref.loadProfile( localProfile ); const LadderPrefMap recentLadders = ladPref.getRecentLadders(); for (LadderPrefMap::const_iterator cit = recentLadders.begin(); cit != recentLadders.end(); ++cit) { AsciiString addr = cit->second.address; UnsignedShort port = cit->second.port; if (addr == lastLadderAddr && port == lastLadderPort) continue; const LadderInfo *info = TheLadderList->findLadder( addr, port ); if (info && info->validCustom && usedLadders.find(info) == usedLadders.end()) { usedLadders.insert(info); index = GadgetComboBoxAddEntry( comboBoxLadderName, info->name, normalColor ); GadgetComboBoxSetItemData( comboBoxLadderName, index, (void *)(info->index) ); } } index = GadgetComboBoxAddEntry( comboBoxLadderName, TheGameText->fetch("GUI:ChooseLadder"), normalColor ); GadgetComboBoxSetItemData( comboBoxLadderName, index, (void *)-1 ); GadgetComboBoxSetSelectedPos( comboBoxLadderName, selectedPos ); isPopulatingLadderBox = false; } // Window Functions ----------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /** Initialize the PopupHostGameInit menu */ //------------------------------------------------------------------------------------------------- void PopupHostGameInit( WindowLayout *layout, void *userData ) { parentPopupID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:ParentHostPopUp")); parentPopup = TheWindowManager->winGetWindowFromId(NULL, parentPopupID); textEntryGameNameID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:TextEntryGameName")); textEntryGameName = TheWindowManager->winGetWindowFromId(parentPopup, textEntryGameNameID); UnicodeString name; name.translate(TheGameSpyInfo->getLocalName()); GadgetTextEntrySetText(textEntryGameName, name); textEntryGameDescriptionID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:TextEntryGameDescription")); textEntryGameDescription = TheWindowManager->winGetWindowFromId(parentPopup, textEntryGameDescriptionID); GadgetTextEntrySetText(textEntryGameDescription, UnicodeString::TheEmptyString); textEntryLadderPasswordID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:TextEntryLadderPassword")); textEntryLadderPassword = TheWindowManager->winGetWindowFromId(parentPopup, textEntryLadderPasswordID); GadgetTextEntrySetText(textEntryLadderPassword, UnicodeString::TheEmptyString); textEntryGamePasswordID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:TextEntryGamePassword")); textEntryGamePassword = TheWindowManager->winGetWindowFromId(parentPopup, textEntryGamePasswordID); GadgetTextEntrySetText(textEntryGamePassword, UnicodeString::TheEmptyString); buttonCreateGameID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:ButtonCreateGame")); buttonCreateGame = TheWindowManager->winGetWindowFromId(parentPopup, buttonCreateGameID); buttonCancelID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:ButtonCancel")); buttonCancel = TheWindowManager->winGetWindowFromId(parentPopup, buttonCancelID); checkBoxAllowObserversID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:CheckBoxAllowObservers")); checkBoxAllowObservers = TheWindowManager->winGetWindowFromId(parentPopup, checkBoxAllowObserversID); CustomMatchPreferences customPref; // disabling observers for Multiplayer test #ifndef _PLAYTEST GadgetCheckBoxSetChecked(checkBoxAllowObservers, customPref.allowsObservers()); #else if (checkBoxAllowObservers) { GadgetCheckBoxSetChecked(checkBoxAllowObservers, FALSE); checkBoxAllowObservers->winEnable(FALSE); } #endif comboBoxLadderNameID = TheNameKeyGenerator->nameToKey(AsciiString("PopupHostGame.wnd:ComboBoxLadderName")); comboBoxLadderName = TheWindowManager->winGetWindowFromId(parentPopup, comboBoxLadderNameID); if (comboBoxLadderName) GadgetComboBoxReset(comboBoxLadderName); PopulateCustomLadderComboBox(); TheWindowManager->winSetFocus( parentPopup ); TheWindowManager->winSetModal( parentPopup ); } //------------------------------------------------------------------------------------------------- /** PopupHostGameInput callback */ //------------------------------------------------------------------------------------------------- WindowMsgHandledType PopupHostGameInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { switch( msg ) { // -------------------------------------------------------------------------------------------- case GWM_CHAR: { UnsignedByte key = mData1; UnsignedByte state = mData2; // if (buttonPushed) // break; switch( key ) { // ---------------------------------------------------------------------------------------- case KEY_ESC: { // // send a simulated selected event to the parent window of the // back/exit button // if( BitTest( state, KEY_STATE_UP ) ) { TheWindowManager->winSendSystemMsg( window, GBM_SELECTED, (WindowMsgData)buttonCancel, buttonCancelID ); } // end if // don't let key fall through anywhere else return MSG_HANDLED; } // end escape } // end switch( key ) } // end char } // end switch( msg ) return MSG_IGNORED; } //------------------------------------------------------------------------------------------------- /** PopupHostGameSystem callback */ //------------------------------------------------------------------------------------------------- WindowMsgHandledType PopupHostGameSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 ) { switch( msg ) { // -------------------------------------------------------------------------------------------- case GWM_CREATE: { break; } // end create //--------------------------------------------------------------------------------------------- case GWM_DESTROY: { parentPopup = NULL; break; } // end case //---------------------------------------------------------------------------------------------- case GWM_INPUT_FOCUS: { // if we're givin the opportunity to take the keyboard focus we must say we want it if( mData1 == TRUE ) *(Bool *)mData2 = TRUE; break; } // end input //---------------------------------------------------------------------------------------------- case GEM_UPDATE_TEXT: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); if ( controlID == textEntryGameNameID ) { UnicodeString txtInput; // grab the game's name txtInput.set(GadgetTextEntryGetText( textEntryGameName )); // Clean up the text (remove leading/trailing chars, etc) const WideChar *c = txtInput.str(); while (c && (iswspace(*c))) c++; if (c) txtInput = UnicodeString(c); else txtInput = UnicodeString::TheEmptyString; // Put the whitespace-free version in the box GadgetTextEntrySetText( textEntryGameName, txtInput ); }// if ( controlID == textEntryPlayerNameID ) break; }//case GEM_UPDATE_TEXT: //--------------------------------------------------------------------------------------------- case GCM_SELECTED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); Int pos = -1; GadgetComboBoxGetSelectedPos(control, &pos); if (controlID == comboBoxLadderNameID && !isPopulatingLadderBox) { if (pos >= 0) { Int ladderID = (Int)GadgetComboBoxGetItemData(control, pos); if (ladderID < 0) { // "Choose a ladder" selected - open overlay PopulateCustomLadderComboBox(); // this restores the non-"Choose a ladder" selection GameSpyOpenOverlay( GSOVERLAY_LADDERSELECT ); } } } break; } // case GCM_SELECTED //--------------------------------------------------------------------------------------------- case GBM_SELECTED: { GameWindow *control = (GameWindow *)mData1; Int controlID = control->winGetWindowId(); if( controlID == buttonCancelID ) { parentPopup = NULL; GameSpyCloseOverlay(GSOVERLAY_GAMEOPTIONS); SetLobbyAttemptHostJoin( FALSE ); } else if( controlID == buttonCreateGameID) { UnicodeString name; name = GadgetTextEntryGetText(textEntryGameName); name.trim(); if(name.getLength() <= 0) { name.translate(TheGameSpyInfo->getLocalName()); GadgetTextEntrySetText(textEntryGameName, name); } createGame(); parentPopup = NULL; GameSpyCloseOverlay(GSOVERLAY_GAMEOPTIONS); } break; } default: return MSG_IGNORED; } // end switch return MSG_HANDLED; } //----------------------------------------------------------------------------- // PRIVATE FUNCTIONS ////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- void createGame( void ) { TheGameSpyInfo->setCurrentGroupRoom(0); PeerRequest req; UnicodeString gameName = GadgetTextEntryGetText(textEntryGameName); req.peerRequestType = PeerRequest::PEERREQUEST_CREATESTAGINGROOM; req.text = gameName.str(); TheGameSpyGame->setGameName(gameName); AsciiString passwd; passwd.translate(GadgetTextEntryGetText(textEntryGamePassword)); req.password = passwd.str(); CustomMatchPreferences customPref; Bool aO = GadgetCheckBoxIsChecked(checkBoxAllowObservers); customPref.setAllowsObserver(aO); customPref.write(); req.stagingRoomCreation.allowObservers = aO; TheGameSpyGame->setAllowObservers(aO); req.stagingRoomCreation.exeCRC = TheGlobalData->m_exeCRC; req.stagingRoomCreation.iniCRC = TheGlobalData->m_iniCRC; req.stagingRoomCreation.gameVersion = TheGameSpyInfo->getInternalIP(); req.stagingRoomCreation.restrictGameList = TheGameSpyConfig->restrictGamesToLobby(); Int ladderSelectPos = -1, ladderID = -1; GadgetComboBoxGetSelectedPos(comboBoxLadderName, &ladderSelectPos); req.ladderIP = "localhost"; req.stagingRoomCreation.ladPort = 0; if (ladderSelectPos >= 0) { ladderID = (Int)GadgetComboBoxGetItemData(comboBoxLadderName, ladderSelectPos); if (ladderID != 0) { // actual ladder const LadderInfo *info = TheLadderList->findLadderByIndex(ladderID); if (info) { req.ladderIP = info->address.str(); req.stagingRoomCreation.ladPort = info->port; } } } TheGameSpyGame->setLadderIP(req.ladderIP.c_str()); TheGameSpyGame->setLadderPort(req.stagingRoomCreation.ladPort); req.hostPingStr = TheGameSpyInfo->getPingString().str(); TheGameSpyPeerMessageQueue->addRequest(req); }