diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 0f36ff63383..66c5666acfe 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -547,6 +547,7 @@ set(GAMEENGINE_SRC Include/GameNetwork/NetCommandWrapperList.h Include/GameNetwork/NetPacket.h Include/GameNetwork/NetPacketStructs.h + Include/GameNetwork/NetworkAutoStart.h Include/GameNetwork/NetworkDefs.h Include/GameNetwork/NetworkInterface.h Include/GameNetwork/networkutil.h @@ -1141,6 +1142,7 @@ set(GAMEENGINE_SRC Source/GameNetwork/NetPacket.cpp Source/GameNetwork/NetPacketStructs.cpp Source/GameNetwork/Network.cpp + Source/GameNetwork/NetworkAutoStart.cpp Source/GameNetwork/NetworkUtil.cpp Source/GameNetwork/Transport.cpp Source/GameNetwork/udp.cpp diff --git a/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h index fdc5212253a..88434cc22e1 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h @@ -73,6 +73,7 @@ extern const Color acceptFalseColor; void lanUpdateSlotList(); void updateGameOptions(); void setLANPlayerTooltip(LANPlayer* player); +void StartLANGame(); //Enum is used for the utility function so other windows do not need //to know about controls on LanGameOptions window. diff --git a/Core/GameEngine/Include/GameNetwork/NetworkAutoStart.h b/Core/GameEngine/Include/GameNetwork/NetworkAutoStart.h new file mode 100644 index 00000000000..28bfd9f433d --- /dev/null +++ b/Core/GameEngine/Include/GameNetwork/NetworkAutoStart.h @@ -0,0 +1,78 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** 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 . +*/ + +#pragma once + +#if defined(RTS_DEBUG) + +#include "Common/AsciiString.h" +#include "Common/UnicodeString.h" +#include "GameNetwork/LANAPI.h" + +// TheSuperHackers @feature bobtista 10/08/2026 Automate network match startup +// for multi-instance testing. +class NetworkAutoStart +{ +public: + enum { MIN_EXPECTED_PLAYERS = 1 }; + + enum Mode + { + MODE_NONE, + MODE_DIRECT_CONNECT, + }; + + enum Role + { + ROLE_NONE, + ROLE_HOST, + ROLE_JOIN, + }; + + static Bool setMode(AsciiString mode); + static Bool setHost(Int expectedPlayers); + static Bool setJoin(AsciiString hostAddress); + static Bool setLocalAddress(AsciiString localAddress); + static Bool setPlayerName(AsciiString playerName); + static Bool setMapName(AsciiString mapName); + static Bool setTimeoutSeconds(Int seconds); + + static Bool hasArguments(); + static Bool isEnabled(); + static Bool shouldOpenDirectConnect(); + static void markDirectConnectOpened(); + + static AsciiString getMapName(); + static UnsignedInt getLocalAddress(); + static UnicodeString getPlayerName(); + + static void updateDirectConnect(); + static void updateGameOptions(); + static void onGameCreate(LANAPIInterface::ReturnType result); + static void onGameJoin(LANAPIInterface::ReturnType result); + static void onLocalAddressSet(Bool result); + static void onGameStartFailure(); + static void onGameStart(); + +private: + static Bool validateConfiguration(); + static Bool checkTimeout(); + static void fail(const char *message); +}; + +#endif diff --git a/Core/GameEngine/Source/Common/CommandLine.cpp b/Core/GameEngine/Source/Common/CommandLine.cpp index 772830f0f67..bbbc4ffc4af 100644 --- a/Core/GameEngine/Source/Common/CommandLine.cpp +++ b/Core/GameEngine/Source/Common/CommandLine.cpp @@ -25,6 +25,8 @@ #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine +#include + #include "Common/ArchiveFileSystem.h" #include "Common/CommandLine.h" #include "Common/CRCDebug.h" @@ -35,6 +37,7 @@ #include "GameClient/TerrainVisual.h" // for TERRAIN_LOD_MIN definition #include "GameClient/GameText.h" #include "GameNetwork/NetworkDefs.h" +#include "GameNetwork/NetworkAutoStart.h" #include "WWLib/trim.h" @@ -484,6 +487,101 @@ Int parseYRes(char *args[], int num) } #if defined(RTS_DEBUG) +static Bool parsePositiveInt(const char *text, Int &result) +{ + if (text == nullptr || *text < '0' || *text > '9') + return false; + + UnsignedInt value = 0; + do + { + const UnsignedInt digit = *text - '0'; + if (value > ((UnsignedInt)INT_MAX - digit) / 10u) + return false; + value = value * 10u + digit; + ++text; + } while (*text >= '0' && *text <= '9'); + + if (*text != '\0') + return false; + + result = (Int)value; + return true; +} + +Int parseAutoNetworkMode(char *args[], int num) +{ + if (num > 1 && NetworkAutoStart::setMode(args[1])) + return 2; + + printf("Invalid -autoNetworkMode. Supported value: direct\n"); + exit(1); + return 1; +} + +Int parseAutoNetworkHost(char *args[], int num) +{ + Int expectedPlayers = 0; + if (num > 1 && parsePositiveInt(args[1], expectedPlayers) && NetworkAutoStart::setHost(expectedPlayers)) + return 2; + + printf("Invalid -autoNetworkHost. Pass an expected player count from %d to %d and do not combine it with -autoNetworkJoin.\n", + NetworkAutoStart::MIN_EXPECTED_PLAYERS, MAX_SLOTS); + exit(1); + return 1; +} + +Int parseAutoNetworkJoin(char *args[], int num) +{ + if (num > 1 && NetworkAutoStart::setJoin(args[1])) + return 2; + + printf("Invalid -autoNetworkJoin. Pass a dotted IPv4 host address and do not combine it with -autoNetworkHost.\n"); + exit(1); + return 1; +} + +Int parseAutoNetworkLocalAddress(char *args[], int num) +{ + if (num > 1 && NetworkAutoStart::setLocalAddress(args[1])) + return 2; + + printf("Invalid -autoNetworkLocalAddress. Pass a dotted IPv4 local address.\n"); + exit(1); + return 1; +} + +Int parseAutoNetworkName(char *args[], int num) +{ + if (num > 1 && NetworkAutoStart::setPlayerName(args[1])) + return 2; + + printf("Invalid -autoNetworkName. Pass a non-empty player name.\n"); + exit(1); + return 1; +} + +Int parseAutoNetworkMap(char *args[], int num) +{ + if (num > 1 && NetworkAutoStart::setMapName(args[1])) + return 2; + + printf("Invalid -autoNetworkMap. Pass a non-empty map path.\n"); + exit(1); + return 1; +} + +Int parseAutoNetworkTimeout(char *args[], int num) +{ + Int timeoutSeconds = 0; + if (num > 1 && parsePositiveInt(args[1], timeoutSeconds) && NetworkAutoStart::setTimeoutSeconds(timeoutSeconds)) + return 2; + + printf("Invalid -autoNetworkTimeout. Pass a positive number of seconds.\n"); + exit(1); + return 1; +} + //============================================================================= //============================================================================= Int parseLatencyAverage(char *args[], int num) @@ -1141,11 +1239,23 @@ static CommandLineParam paramsForStartup[] = // (If you have 4 cores, call it with -jobs 4) // If you do not call this, all replays will be simulated in sequence in the same process. { "-jobs", parseJobs }, + +#if defined(RTS_DEBUG) + { "-autoNetworkMode", parseAutoNetworkMode }, +#endif }; // These Params are parsed during Engine Init before INI data is loaded static CommandLineParam paramsForEngineInit[] = { +#if defined(RTS_DEBUG) + { "-autoNetworkHost", parseAutoNetworkHost }, + { "-autoNetworkJoin", parseAutoNetworkJoin }, + { "-autoNetworkLocalAddress", parseAutoNetworkLocalAddress }, + { "-autoNetworkName", parseAutoNetworkName }, + { "-autoNetworkMap", parseAutoNetworkMap }, + { "-autoNetworkTimeout", parseAutoNetworkTimeout }, +#endif { "-nologo", parseNoLogo }, // TheSuperHackers @tweak Is now available in Release builds. { "-noshellmap", parseNoShellMap }, { "-noShellAnim", parseNoWindowAnimation }, // TheSuperHackers @tweak Is now available in Release builds. diff --git a/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index a1ecfbb3238..6f889972434 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp @@ -45,6 +45,7 @@ #include "GameLogic/GameLogic.h" #include "GameNetwork/FileTransfer.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" #include "GameNetwork/networkutil.h" LANAPI *TheLAN = nullptr; @@ -243,6 +244,9 @@ void LANAPI::OnGameStart() if (!filesOk || TheMapCache->findMap(m_currentGame->getMap()) == nullptr) { DEBUG_LOG(("After transfer, we didn't really have the map. Bailing...")); +#if defined(RTS_DEBUG) + NetworkAutoStart::onGameStartFailure(); +#endif OnPlayerLeave(m_name); removeGame(m_currentGame); m_currentGame = nullptr; @@ -271,6 +275,10 @@ void LANAPI::OnGameStart() // Set the seeds InitRandom( m_currentGame->getSeed() ); DEBUG_LOG(("InitRandom( %d )", m_currentGame->getSeed())); + +#if defined(RTS_DEBUG) + NetworkAutoStart::onGameStart(); +#endif } } @@ -515,6 +523,15 @@ void LANAPI::OnPlayerJoin( Int slot, UnicodeString playerName ) void LANAPI::OnGameJoin( ReturnType ret, LANGameInfo *theGame ) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled()) + { + NetworkAutoStart::onGameJoin(ret); + if (ret != RET_OK) + return; + } +#endif + if (ret == RET_OK) { LANbuttonPushed = true; @@ -605,6 +622,15 @@ void LANAPI::OnGameList( LANGameInfo *gameList ) void LANAPI::OnGameCreate( ReturnType ret ) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled()) + { + NetworkAutoStart::onGameCreate(ret); + if (ret != RET_OK) + return; + } +#endif + if (ret == RET_OK) { diff --git a/Core/GameEngine/Source/GameNetwork/NetworkAutoStart.cpp b/Core/GameEngine/Source/GameNetwork/NetworkAutoStart.cpp new file mode 100644 index 00000000000..638fca6293a --- /dev/null +++ b/Core/GameEngine/Source/GameNetwork/NetworkAutoStart.cpp @@ -0,0 +1,512 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** 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 . +*/ + +#include "PreRTS.h" + +#if defined(RTS_DEBUG) + +#include + +#include "GameClient/ClientInstance.h" +#include "GameClient/MapUtil.h" +#include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" + +namespace +{ +enum { + DefaultStartupTimeoutMilliseconds = 30000, + ActionRetryMilliseconds = 1000, + MillisecondsPerSecond = 1000, + IPv4OctetCount = 4, + IPv4BitsPerOctet = 8, + MaxIPv4OctetValue = 255, +}; + +const UnsignedInt IPv4BroadcastAddress = UINT_MAX; + +NetworkAutoStart::Mode s_mode = NetworkAutoStart::MODE_NONE; +NetworkAutoStart::Role s_role = NetworkAutoStart::ROLE_NONE; +Int s_expectedPlayers = 0; +UnsignedInt s_hostAddress = 0; +UnsignedInt s_localAddress = 0; +AsciiString s_playerName; +AsciiString s_mapName; +UnsignedInt s_timeoutMilliseconds = DefaultStartupTimeoutMilliseconds; +UnsignedInt s_startTime = 0; +UnsignedInt s_lastActionTime = 0; +Bool s_hasArguments = false; +Bool s_directConnectOpened = false; +Bool s_actionPending = false; +Bool s_startRequested = false; +Bool s_gameStarted = false; +Bool s_failed = false; + +Bool ParseIPv4Address(AsciiString address, UnsignedInt &result) +{ + const char *cursor = address.str(); + result = 0; + for (Int octet = 0; octet < IPv4OctetCount; ++octet) + { + if (*cursor < '0' || *cursor > '9') + { + return false; + } + + UnsignedInt value = 0; + do + { + value = value * 10 + (*cursor - '0'); + if (value > MaxIPv4OctetValue) + { + return false; + } + ++cursor; + } while (*cursor >= '0' && *cursor <= '9'); + + result = (result << IPv4BitsPerOctet) | value; + if (octet + 1 < IPv4OctetCount) + { + if (*cursor != '.') + { + return false; + } + ++cursor; + } + else if (*cursor != '\0') + { + return false; + } + } + + return result != 0 && result != IPv4BroadcastAddress; +} + +Bool CanAcceptMap(LANGameInfo *game, LANGameSlot *slot) +{ + if (slot->hasMap()) + { + return true; + } + + const MapMetaData *mapData = TheMapCache->findMap(game->getMap()); + if (mapData != nullptr) + { + return !mapData->m_isOfficial; + } + + return WouldMapTransfer(game->getMap()); +} +} // namespace + +Bool NetworkAutoStart::setMode(AsciiString mode) +{ + s_hasArguments = true; + if (mode.compareNoCase("direct") == 0) + { + s_mode = MODE_DIRECT_CONNECT; + rts::ClientInstance::setMultiInstance(true); + rts::ClientInstance::skipPrimaryInstance(); + return true; + } + + return false; +} + +Bool NetworkAutoStart::setHost(Int expectedPlayers) +{ + s_hasArguments = true; + if (s_role == ROLE_JOIN || expectedPlayers < MIN_EXPECTED_PLAYERS || expectedPlayers > MAX_SLOTS) + { + return false; + } + + s_role = ROLE_HOST; + s_expectedPlayers = expectedPlayers; + return true; +} + +Bool NetworkAutoStart::setJoin(AsciiString hostAddress) +{ + s_hasArguments = true; + hostAddress.trim(); + if (s_role == ROLE_HOST || hostAddress.isEmpty()) + { + return false; + } + + UnsignedInt resolvedAddress = 0; + if (!ParseIPv4Address(hostAddress, resolvedAddress)) + { + return false; + } + + s_role = ROLE_JOIN; + s_hostAddress = resolvedAddress; + return true; +} + +Bool NetworkAutoStart::setLocalAddress(AsciiString localAddress) +{ + s_hasArguments = true; + localAddress.trim(); + if (localAddress.isEmpty()) + { + return false; + } + + return ParseIPv4Address(localAddress, s_localAddress); +} + +Bool NetworkAutoStart::setPlayerName(AsciiString playerName) +{ + s_hasArguments = true; + playerName.trim(); + if (playerName.isEmpty()) + { + return false; + } + + s_playerName = playerName; + return true; +} + +Bool NetworkAutoStart::setMapName(AsciiString mapName) +{ + s_hasArguments = true; + mapName.trim(); + if (mapName.isEmpty()) + { + return false; + } + + s_mapName = mapName; + return true; +} + +Bool NetworkAutoStart::setTimeoutSeconds(Int seconds) +{ + s_hasArguments = true; + if (seconds < 1 || (UnsignedInt)seconds > UINT_MAX / MillisecondsPerSecond) + { + return false; + } + + s_timeoutMilliseconds = (UnsignedInt)seconds * MillisecondsPerSecond; + return true; +} + +Bool NetworkAutoStart::hasArguments() +{ + return s_hasArguments; +} + +Bool NetworkAutoStart::isEnabled() +{ + return !s_failed && s_mode != MODE_NONE && s_role != ROLE_NONE; +} + +Bool NetworkAutoStart::validateConfiguration() +{ + if (s_failed) + { + return false; + } + + if (s_mode == MODE_NONE) + { + fail("-autoNetworkMode direct is required"); + return false; + } + + if (s_role == ROLE_NONE) + { + fail("either -autoNetworkHost or -autoNetworkJoin is required"); + return false; + } + + return true; +} + +Bool NetworkAutoStart::shouldOpenDirectConnect() +{ + if (!s_hasArguments || s_directConnectOpened || !validateConfiguration()) + { + return false; + } + + return s_mode == MODE_DIRECT_CONNECT; +} + +void NetworkAutoStart::markDirectConnectOpened() +{ + s_directConnectOpened = true; + if (s_startTime == 0) + { + s_startTime = timeGetTime(); + } +} + +AsciiString NetworkAutoStart::getMapName() +{ + return s_mapName; +} + +UnsignedInt NetworkAutoStart::getLocalAddress() +{ + return s_localAddress; +} + +UnicodeString NetworkAutoStart::getPlayerName() +{ + UnicodeString name; + if (s_playerName.isNotEmpty()) + { + name.translate(s_playerName); + } + else + { + name.format(L"AutoNet%02u", rts::ClientInstance::getInstanceId()); + } + name.truncateTo(g_lanPlayerNameLength); + return name; +} + +Bool NetworkAutoStart::checkTimeout() +{ + if (s_failed || s_gameStarted) + { + return true; + } + + const UnsignedInt now = timeGetTime(); + if (s_startTime != 0 && now - s_startTime >= s_timeoutMilliseconds) + { + fail("network match startup timed out"); + return true; + } + + return false; +} + +void NetworkAutoStart::fail(const char *message) +{ + if (s_failed) + { + return; + } + + s_failed = true; + s_actionPending = false; + DEBUG_LOG(("NetworkAutoStart failed: %s", message)); + printf("NetworkAutoStart failed: %s\n", message); +} + +void NetworkAutoStart::updateDirectConnect() +{ + if (!isEnabled() || s_mode != MODE_DIRECT_CONNECT || checkTimeout() || TheLAN == nullptr) + { + return; + } + + const UnsignedInt now = timeGetTime(); + if (s_actionPending || (s_lastActionTime != 0 && now - s_lastActionTime < ActionRetryMilliseconds)) + { + return; + } + + TheLAN->RequestSetName(getPlayerName()); + s_lastActionTime = now; + s_actionPending = true; + + if (s_role == ROLE_HOST) + { + DEBUG_LOG(("NetworkAutoStart creating Direct Connect game for %d players", s_expectedPlayers)); + TheLAN->RequestGameCreate(UnicodeString::TheEmptyString, true); + } + else + { + DEBUG_LOG(("NetworkAutoStart joining Direct Connect host 0x%08X", s_hostAddress)); + TheLAN->RequestGameJoinDirectConnect(s_hostAddress); + } +} + +void NetworkAutoStart::updateGameOptions() +{ + if (!isEnabled() || checkTimeout() || TheLAN == nullptr || TheLAN->GetMyGame() == nullptr) + { + return; + } + + LANGameInfo *game = TheLAN->GetMyGame(); + if (s_role == ROLE_JOIN) + { + const Int localSlot = game->getLocalSlotNum(); + if (localSlot < 0) + { + return; + } + + LANGameSlot *slot = game->getLANSlot(localSlot); + const UnsignedInt now = timeGetTime(); + if (slot != nullptr && !slot->isAccepted() && + (s_lastActionTime == 0 || now - s_lastActionTime >= ActionRetryMilliseconds)) + { + TheLAN->RequestHasMap(); + if (!CanAcceptMap(game, slot)) + { + fail("required map is unavailable and cannot be transferred"); + return; + } + + TheLAN->RequestAccept(); + s_lastActionTime = now; + } + return; + } + + if (s_startRequested) + { + return; + } + + const MapMetaData *mapData = TheMapCache->findMap(game->getMap()); + if (mapData == nullptr) + { + fail("selected map was not found"); + return; + } + if (mapData->m_numPlayers < s_expectedPlayers) + { + fail("selected map has fewer slots than -autoNetworkHost requires"); + return; + } + + Int humanPlayers = 0; + for (Int humanIndex = 0; humanIndex < MAX_SLOTS; ++humanIndex) + { + LANGameSlot *slot = game->getLANSlot(humanIndex); + if (slot != nullptr && slot->isHuman()) + { + ++humanPlayers; + } + } + + if (humanPlayers > s_expectedPlayers) + { + fail("more players joined than -autoNetworkHost expects"); + return; + } + if (humanPlayers != s_expectedPlayers) + { + return; + } + + LANGameSlot *hostSlot = game->getLANSlot(0); + if (hostSlot == nullptr) + { + fail("host slot is unavailable"); + return; + } + hostSlot->setAccept(); + for (Int acceptedIndex = 0; acceptedIndex < MAX_SLOTS; ++acceptedIndex) + { + LANGameSlot *slot = game->getLANSlot(acceptedIndex); + if (slot != nullptr && slot->isHuman() && !slot->isAccepted()) + { + return; + } + } + + const UnsignedInt now = timeGetTime(); + if (s_lastActionTime == 0 || now - s_lastActionTime >= ActionRetryMilliseconds) + { + DEBUG_LOG(("NetworkAutoStart starting Direct Connect game with %d players", humanPlayers)); + s_lastActionTime = now; + s_startRequested = true; + StartLANGame(); + } +} + +void NetworkAutoStart::onGameCreate(LANAPIInterface::ReturnType result) +{ + if (!isEnabled()) + { + return; + } + + if (result == LANAPIInterface::RET_OK) + { + return; + } + + s_actionPending = false; + fail("could not create Direct Connect game"); +} + +void NetworkAutoStart::onGameJoin(LANAPIInterface::ReturnType result) +{ + if (!isEnabled()) + { + return; + } + + if (result == LANAPIInterface::RET_OK) + { + return; + } + + s_actionPending = false; + if (result == LANAPIInterface::RET_TIMEOUT || result == LANAPIInterface::RET_GAME_GONE) + { + DEBUG_LOG(("NetworkAutoStart will retry Direct Connect join after result %d", result)); + return; + } + + fail("Direct Connect join was rejected"); +} + +void NetworkAutoStart::onLocalAddressSet(Bool result) +{ + if (isEnabled() && !result) + { + fail("could not bind the Direct Connect local address"); + } +} + +void NetworkAutoStart::onGameStartFailure() +{ + if (isEnabled()) + { + fail("required map could not be transferred"); + } +} + +void NetworkAutoStart::onGameStart() +{ + if (!isEnabled()) + { + return; + } + + s_gameStarted = true; + DEBUG_LOG(("NetworkAutoStart entered the network game")); + printf("NetworkAutoStart entered the network game\n"); +} + +#endif diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp index 0520332611d..ca821037df5 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -53,6 +53,7 @@ #include "GameNetwork/LANAPI.h" #include "GameNetwork/IPEnumeration.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" @@ -211,7 +212,7 @@ static void playerTooltip(GameWindow *window, setLANPlayerTooltip(player); } -void StartPressed() +void StartLANGame() { LANGameInfo *myGame = TheLAN->GetMyGame(); @@ -780,8 +781,13 @@ void LanGameOptionsMenuInit( WindowLayout *layout, void *userData ) slot->setColor( pref.getPreferredColor() ); slot->setPlayerTemplate( pref.getPreferredFaction() ); slot->setNATBehavior(FirewallHelperClass::FIREWALL_TYPE_SIMPLE); - game->setMap( pref.getPreferredMap() ); - AsciiString lowerMap = pref.getPreferredMap(); + AsciiString mapName = pref.getPreferredMap(); +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && NetworkAutoStart::getMapName().isNotEmpty()) + mapName = NetworkAutoStart::getMapName(); +#endif + game->setMap(mapName); + AsciiString lowerMap = mapName; lowerMap.toLower(); std::map::iterator it = TheMapCache->find(lowerMap); if (it != TheMapCache->end()) @@ -975,6 +981,14 @@ void LanGameOptionsMenuShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void LanGameOptionsMenuUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + TheLAN->update(); + NetworkAutoStart::updateGameOptions(); + } +#endif + if(LANisShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout); //TheLAN->update(); // this is handled in the lobby @@ -1160,7 +1174,7 @@ WindowMsgHandledType LanGameOptionsMenuSystem( GameWindow *window, UnsignedInt m { if (TheLAN->AmIHost()) { - StartPressed(); + StartLANGame(); //TheLAN->RequestGameStart(); } else diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp index 0097330bb16..fd6e2d30480 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp @@ -69,6 +69,7 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" +#include "GameNetwork/NetworkAutoStart.h" #include "GameClient/InGameUI.h" @@ -748,6 +749,15 @@ void ResolutionDialogUpdate() void DownloadMenuUpdate( WindowLayout *layout, void *userData ); void MainMenuUpdate( WindowLayout *layout, void *userData ) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::shouldOpenDirectConnect()) + { + NetworkAutoStart::markDirectConnectOpened(); + TheShell->push("Menus/NetworkDirectConnect.wnd"); + return; + } +#endif + if( TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() ) { return; diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp index 1412088c582..400cfcfc91d 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp @@ -49,6 +49,7 @@ #include "GameNetwork/IPEnumeration.h" #include "GameNetwork/LANAPI.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" // window ids ------------------------------------------------------------------------------ @@ -252,12 +253,25 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) LANbuttonPushed = false; LANisShuttingDown = false; - if (TheLAN == nullptr) + Bool automatedStartup = FALSE; + UnsignedInt autoLocalIP = 0; +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled()) { - TheLAN = NEW LANAPI(); - TheLAN->init(); + automatedStartup = TRUE; + autoLocalIP = NetworkAutoStart::getLocalAddress(); + } +#endif + + if (!automatedStartup) + { + if (TheLAN == nullptr) + { + TheLAN = NEW LANAPI(); + TheLAN->init(); + } + TheLAN->reset(); } - TheLAN->reset(); buttonPushed = false; isShuttingDown = false; @@ -305,6 +319,8 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) OptionPreferences prefs; UnsignedInt IP = prefs.getOnlineIPAddress(); + if (autoLocalIP != 0) + IP = autoLocalIP; IPEnumeration IPs; @@ -317,7 +333,7 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) /// @todo: display error and exit lan lobby if no IPs are found } - Bool foundIP = FALSE; + Bool foundIP = autoLocalIP != 0; EnumeratedIP *tempIP = IPlist; while ((tempIP != nullptr) && (foundIP == FALSE)) { if (IP == tempIP->getIP()) { @@ -333,8 +349,17 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) // IP = IPlist->getIP(); // } - TheLAN->init(); - TheLAN->SetLocalIP(IP); + if (automatedStartup) + { +#if defined(RTS_DEBUG) + NetworkAutoStart::onLocalAddressSet(TheLAN->SetLocalIP(IP)); +#endif + } + else + { + TheLAN->init(); + TheLAN->SetLocalIP(IP); + } } UnsignedInt ip = TheLAN->GetLocalIP(); @@ -393,6 +418,14 @@ void NetworkDirectConnectShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void NetworkDirectConnectUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + TheLAN->update(); + NetworkAutoStart::updateDirectConnect(); + } +#endif + // We'll only be successful if we've requested to if(isShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp index 609a0f772cd..f6d43ea874a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -56,6 +56,7 @@ #include "GameNetwork/LANAPI.h" #include "GameNetwork/IPEnumeration.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" @@ -218,7 +219,7 @@ static void playerTooltip(GameWindow *window, setLANPlayerTooltip(player); } -void StartPressed() +void StartLANGame() { LANGameInfo *myGame = TheLAN->GetMyGame(); @@ -856,10 +857,15 @@ void LanGameOptionsMenuInit( WindowLayout *layout, void *userData ) slot->setColor( pref.getPreferredColor() ); slot->setPlayerTemplate( pref.getPreferredFaction() ); slot->setNATBehavior(FirewallHelperClass::FIREWALL_TYPE_SIMPLE); - game->setMap( pref.getPreferredMap() ); + AsciiString mapName = pref.getPreferredMap(); +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && NetworkAutoStart::getMapName().isNotEmpty()) + mapName = NetworkAutoStart::getMapName(); +#endif + game->setMap(mapName); game->setStartingCash( pref.getStartingCash() ); game->setSuperweaponRestriction( pref.getSuperweaponRestricted() ? 1 : 0 ); - AsciiString lowerMap = pref.getPreferredMap(); + AsciiString lowerMap = mapName; lowerMap.toLower(); std::map::iterator it = TheMapCache->find(lowerMap); if (it != TheMapCache->end()) @@ -1070,6 +1076,14 @@ void LanGameOptionsMenuShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void LanGameOptionsMenuUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + TheLAN->update(); + NetworkAutoStart::updateGameOptions(); + } +#endif + if(LANisShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout); //TheLAN->update(); // this is handled in the lobby @@ -1263,7 +1277,7 @@ WindowMsgHandledType LanGameOptionsMenuSystem( GameWindow *window, UnsignedInt m { if (TheLAN->AmIHost()) { - StartPressed(); + StartLANGame(); //TheLAN->RequestGameStart(); } else diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp index f51953e454f..1b63af43e9d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/MainMenu.cpp @@ -72,6 +72,7 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" +#include "GameNetwork/NetworkAutoStart.h" #include "GameClient/InGameUI.h" @@ -785,6 +786,15 @@ void ResolutionDialogUpdate() void DownloadMenuUpdate( WindowLayout *layout, void *userData ); void MainMenuUpdate( WindowLayout *layout, void *userData ) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::shouldOpenDirectConnect()) + { + NetworkAutoStart::markDirectConnectOpened(); + TheShell->push("Menus/NetworkDirectConnect.wnd"); + return; + } +#endif + if( TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() ) { return; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp index 7c9c462f9b9..9afa817e837 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/NetworkDirectConnect.cpp @@ -49,6 +49,7 @@ #include "GameNetwork/IPEnumeration.h" #include "GameNetwork/LANAPI.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" // window ids ------------------------------------------------------------------------------ @@ -252,12 +253,25 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) LANbuttonPushed = false; LANisShuttingDown = false; - if (TheLAN == nullptr) + Bool automatedStartup = FALSE; + UnsignedInt autoLocalIP = 0; +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled()) { - TheLAN = NEW LANAPI(); - TheLAN->init(); + automatedStartup = TRUE; + autoLocalIP = NetworkAutoStart::getLocalAddress(); + } +#endif + + if (!automatedStartup) + { + if (TheLAN == nullptr) + { + TheLAN = NEW LANAPI(); + TheLAN->init(); + } + TheLAN->reset(); } - TheLAN->reset(); buttonPushed = false; isShuttingDown = false; @@ -305,6 +319,8 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) OptionPreferences prefs; UnsignedInt IP = prefs.getOnlineIPAddress(); + if (autoLocalIP != 0) + IP = autoLocalIP; IPEnumeration IPs; @@ -317,7 +333,7 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) /// @todo: display error and exit lan lobby if no IPs are found } - Bool foundIP = FALSE; + Bool foundIP = autoLocalIP != 0; EnumeratedIP *tempIP = IPlist; while ((tempIP != nullptr) && (foundIP == FALSE)) { if (IP == tempIP->getIP()) { @@ -333,8 +349,17 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) // IP = IPlist->getIP(); // } - TheLAN->init(); - TheLAN->SetLocalIP(IP); + if (automatedStartup) + { +#if defined(RTS_DEBUG) + NetworkAutoStart::onLocalAddressSet(TheLAN->SetLocalIP(IP)); +#endif + } + else + { + TheLAN->init(); + TheLAN->SetLocalIP(IP); + } } UnsignedInt ip = TheLAN->GetLocalIP(); @@ -393,6 +418,14 @@ void NetworkDirectConnectShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void NetworkDirectConnectUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + TheLAN->update(); + NetworkAutoStart::updateDirectConnect(); + } +#endif + // We'll only be successful if we've requested to if(isShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout);