diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index c462ee691f2..9d665ce8282 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -549,6 +549,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 @@ -1145,6 +1146,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/GameClient/MapUtil.h b/Core/GameEngine/Include/GameClient/MapUtil.h index 7cb5c71b9db..fbf34148894 100644 --- a/Core/GameEngine/Include/GameClient/MapUtil.h +++ b/Core/GameEngine/Include/GameClient/MapUtil.h @@ -148,3 +148,4 @@ Bool parseMapPreviewChunk(DataChunkInput &file, DataChunkInfo *info, void *userD void findDrawPositions( Int startX, Int startY, Int width, Int height, Region3D extent, ICoord2D *ul, ICoord2D *lr ); Bool WouldMapTransfer( const AsciiString& mapName ); +Bool CanTransferMap(const AsciiString &mapName); diff --git a/Core/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h index df22c116d9f..9371fd5c4d1 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPI.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPI.h @@ -283,6 +283,7 @@ class LANAPI : public LANAPIInterface virtual ~LANAPI() override; virtual void init() override; ///< Initialize or re-initialize the instance + Bool init(UnsignedInt localIP); virtual void reset() override; ///< reset the logic system virtual void update() override; ///< update the world diff --git a/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h index fdc5212253a..9f4ca2b6ee2 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); +Bool 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..472623d5b18 --- /dev/null +++ b/Core/GameEngine/Include/GameNetwork/NetworkAutoStart.h @@ -0,0 +1,79 @@ +/* +** 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" + +// 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(const 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 isEnabled(); + static Bool hasFailed(); + static Bool shouldOpenLobby(); + static void markLobbyOpened(); + 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/Include/GameNetwork/networkutil.h b/Core/GameEngine/Include/GameNetwork/networkutil.h index dc9455aca55..f516ad5839c 100644 --- a/Core/GameEngine/Include/GameNetwork/networkutil.h +++ b/Core/GameEngine/Include/GameNetwork/networkutil.h @@ -29,6 +29,8 @@ UnsignedInt AssembleIp(UnsignedByte a, UnsignedByte b, UnsignedByte c, UnsignedByte d); UnsignedInt ResolveIP(AsciiString host); +// Parse four decimal IPv4 octets, excluding unspecified and broadcast addresses. +Bool ParseIPv4Address(const AsciiString &address, UnsignedInt &result); UnsignedShort GenerateNextCommandID(); Bool DoesCommandRequireACommandID(NetCommandType type); Bool CommandRequiresAck(const NetCommandMsg *msg); diff --git a/Core/GameEngine/Source/Common/CommandLine.cpp b/Core/GameEngine/Source/Common/CommandLine.cpp index 0daeb20e9ff..64a420c006e 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" @@ -36,6 +38,7 @@ #include "GameClient/TerrainVisual.h" // for TERRAIN_LOD_MIN definition #include "GameClient/GameText.h" #include "GameNetwork/NetworkDefs.h" +#include "GameNetwork/NetworkAutoStart.h" @@ -502,6 +505,121 @@ Int parseYRes(char *args[], int num) } #if defined(RTS_DEBUG) +static Bool parseNonNegativeInt(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 && parseNonNegativeInt(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 && parseNonNegativeInt(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) @@ -1179,11 +1297,23 @@ static CommandLineParam paramsForStartup[] = // The last successful selection wins; otherwise use the executable directory. { "-setCwd", parseSetCwd }, { "-useCwd", parseUseCwd }, +#if defined(RTS_DEBUG) + // TheSuperHackers @feature bobtista 10/08/2026 Automate network match startup for multi-instance testing. + { "-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/GameClient/MapUtil.cpp b/Core/GameEngine/Source/GameClient/MapUtil.cpp index 1806c50741a..a1e1cfbd937 100644 --- a/Core/GameEngine/Source/GameClient/MapUtil.cpp +++ b/Core/GameEngine/Source/GameClient/MapUtil.cpp @@ -721,6 +721,16 @@ Bool WouldMapTransfer( const AsciiString& mapName ) return mapName.startsWithNoCase(TheMapCache->getUserMapDir()); } +Bool CanTransferMap(const AsciiString &mapName) +{ + const MapMetaData *mapData = TheMapCache->findMap(mapName); + if (mapData != nullptr) + { + return !mapData->m_isOfficial; + } + return WouldMapTransfer(mapName); +} + //------------------------------------------------------------------------------------------------- typedef std::set/**/> MapNameList; typedef std::map MapDisplayToFileNameList; diff --git a/Core/GameEngine/Source/GameNetwork/LANAPI.cpp b/Core/GameEngine/Source/GameNetwork/LANAPI.cpp index 8cbfbdea6c5..90dfc48e7cd 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -96,12 +96,15 @@ LANAPI::~LANAPI() } void LANAPI::init() +{ + init(m_localIP); +} + +Bool LANAPI::init(UnsignedInt localIP) { m_gameStartTime = 0; m_gameStartSeconds = 0; - m_transport->reset(); - m_transport->init(m_localIP, lobbyPort); - m_transport->allowBroadcasts(true); + const Bool bound = SetLocalIP(localIP); m_pendingAction = ACT_NONE; m_expiration = 0; @@ -135,6 +138,7 @@ void LANAPI::init() m_hostName = "unknown"; } #endif + return bound; } void LANAPI::reset() @@ -760,17 +764,14 @@ void LANAPI::RequestHasMap() UnicodeString text; UnicodeString mapDisplayName; const MapMetaData *mapData = TheMapCache->findMap( m_currentGame->getMap() ); - Bool willTransfer = TRUE; + Bool willTransfer = CanTransferMap(m_currentGame->getMap()); if (mapData) { mapDisplayName.format(L"%ls", mapData->m_displayName.str()); - if (mapData->m_isOfficial) - willTransfer = FALSE; } else { mapDisplayName.format(L"%hs", TheGameState->getMapLeafName(m_currentGame->getMap()).str()); - willTransfer = WouldMapTransfer(m_currentGame->getMap()); } if (willTransfer) text.format(TheGameText->fetch("GUI:LocalPlayerNoMapWillTransfer"), mapDisplayName.str()); diff --git a/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index a1ecfbb3238..c44ec3784b4 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; @@ -158,17 +159,14 @@ void LANAPI::OnHasMap( UnsignedInt playerIP, Bool status ) { UnicodeString mapDisplayName; const MapMetaData *mapData = TheMapCache->findMap( m_currentGame->getMap() ); - Bool willTransfer = TRUE; + Bool willTransfer = CanTransferMap(m_currentGame->getMap()); if (mapData) { mapDisplayName.format(L"%ls", mapData->m_displayName.str()); - if (mapData->m_isOfficial) - willTransfer = FALSE; } else { mapDisplayName.format(L"%hs", m_currentGame->getMap().str()); - willTransfer = WouldMapTransfer(m_currentGame->getMap()); } if (!status) { @@ -243,6 +241,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 +272,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 +520,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 +619,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..3abffe8ca9b --- /dev/null +++ b/Core/GameEngine/Source/GameNetwork/NetworkAutoStart.cpp @@ -0,0 +1,472 @@ +/* +** 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 "Common/GameEngine.h" +#include "GameClient/ClientInstance.h" +#include "GameClient/MapUtil.h" +#include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" +#include "GameNetwork/networkutil.h" + +namespace +{ +enum { + DefaultStartupTimeoutMilliseconds = 30000, + ActionRetryMilliseconds = 1000, + MillisecondsPerSecond = 1000, +}; + +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_lobbyOpened = false; +Bool s_directConnectOpened = false; +Bool s_actionPending = false; +Bool s_startRequested = false; +Bool s_gameStarted = false; +Bool s_failed = false; + +} // namespace + +Bool NetworkAutoStart::setMode(const 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::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::hasFailed() +{ + return s_failed; +} + +Bool NetworkAutoStart::shouldOpenLobby() +{ + return s_hasArguments && !s_lobbyOpened && validateConfiguration(); +} + +void NetworkAutoStart::markLobbyOpened() +{ + s_lobbyOpened = true; + s_startTime = timeGetTime(); +} + +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); + fflush(stdout); + TheGameEngine->setQuitting(true); +} + +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 (!slot->hasMap() && !CanTransferMap(game->getMap())) + { + 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 = StartLANGame(); + if (!s_startRequested) + { + fail("LAN start validation rejected the match; see LAN system messages"); + } + } +} + +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 requested network game startup")); + printf("NetworkAutoStart requested network game startup\n"); + fflush(stdout); +} + +#endif diff --git a/Core/GameEngine/Source/GameNetwork/NetworkUtil.cpp b/Core/GameEngine/Source/GameNetwork/NetworkUtil.cpp index b709e670d7e..86aeb85c7c7 100644 --- a/Core/GameEngine/Source/GameNetwork/NetworkUtil.cpp +++ b/Core/GameEngine/Source/GameNetwork/NetworkUtil.cpp @@ -67,6 +67,46 @@ void dumpBufferToLog(const void *vBuf, Int len, const char *fname, Int line) #endif // DEBUG_LOGGING +Bool ParseIPv4Address(const AsciiString &address, UnsignedInt &result) +{ + const char *cursor = address.str(); + result = 0; + for (Int octet = 0; octet < 4; ++octet) + { + if (*cursor < '0' || *cursor > '9') + { + return false; + } + + UnsignedInt value = 0; + do + { + value = value * 10 + (*cursor - '0'); + if (value > 255) + { + return false; + } + ++cursor; + } while (*cursor >= '0' && *cursor <= '9'); + + result = (result << 8) | value; + if (octet + 1 < 4) + { + if (*cursor != '.') + { + return false; + } + ++cursor; + } + else if (*cursor != '\0') + { + return false; + } + } + + return result != 0 && result != INADDR_BROADCAST; +} + /** * ResolveIP turns a string ("games2.westwood.com", or "192.168.0.1") into * a 32-bit unsigned integer. 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..89b39721973 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() +Bool StartLANGame() { LANGameInfo *myGame = TheLAN->GetMyGame(); @@ -220,7 +221,7 @@ void StartPressed() Int playerCount = 0; if (!myGame) { - return; + return false; } myGame->getLANSlot(0)->setAccept(); // cause we are, of course! @@ -249,7 +250,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:TooManyPlayers"), (md)?md->m_numPlayers:0); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for observer + AI players @@ -260,7 +261,7 @@ void StartPressed() UnicodeString text = TheGameText->fetch("GUI:NeedHumanPlayers"); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for too few players @@ -272,7 +273,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:NeedMorePlayers"),numUsers); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for too few teams @@ -301,7 +302,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:NeedMoreTeams")); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } if (numRandom + teams.size() < 2) @@ -363,6 +364,7 @@ void StartPressed() else TheLAN->RequestGameStart(); LANEnableStartButton(false); + return true; } else { @@ -374,6 +376,7 @@ void StartPressed() } } + return false; } void LANEnableStartButton(Bool enabled) @@ -780,8 +783,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 +983,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 +1176,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/Common/GameMain.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp index ed94ec7bf54..aab72f0d297 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp @@ -31,6 +31,8 @@ #include "Common/FramePacer.h" #include "Common/GameEngine.h" #include "Common/ReplaySimulation.h" +#include "GameNetwork/NetworkAutoStart.h" +#include "GameNetwork/LANAPICallbacks.h" /** @@ -55,6 +57,19 @@ Int GameMain() TheGameEngine->execute(); } +#if defined(RTS_DEBUG) + if (NetworkAutoStart::hasFailed()) + { + if (TheLAN != nullptr && TheLAN->GetMyGame() != nullptr) + { + TheLAN->RequestGameLeave(); + } + delete TheLAN; + TheLAN = nullptr; + exitcode = 1; + } +#endif + // since execute() returned, we are exiting the game delete TheFramePacer; TheFramePacer = nullptr; 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..b33d9f844aa 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() +Bool StartLANGame() { LANGameInfo *myGame = TheLAN->GetMyGame(); @@ -227,7 +228,7 @@ void StartPressed() Int playerCount = 0; if (!myGame) { - return; + return false; } myGame->getLANSlot(0)->setAccept(); // cause we are, of course! @@ -256,7 +257,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:TooManyPlayers"), (md)?md->m_numPlayers:0); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for observer + AI players @@ -267,7 +268,7 @@ void StartPressed() UnicodeString text = TheGameText->fetch("GUI:NeedHumanPlayers"); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for too few players @@ -279,7 +280,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:NeedMorePlayers"),numUsers); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } // Check for too few teams @@ -308,7 +309,7 @@ void StartPressed() text.format(TheGameText->fetch("LAN:NeedMoreTeams")); TheLAN->OnChat(L"SYSTEM", TheLAN->GetLocalIP(), text, LANAPI::LANCHAT_SYSTEM); } - return; + return false; } if (numRandom + teams.size() < 2) @@ -321,16 +322,14 @@ void StartPressed() // see if everyone's accepted and count the number of players in the game UnicodeString mapDisplayName; const MapMetaData *mapData = TheMapCache->findMap( myGame->getMap() ); - Bool willTransfer = TRUE; + Bool willTransfer = CanTransferMap(myGame->getMap()); if (mapData) { mapDisplayName.format(L"%ls", mapData->m_displayName.str()); - willTransfer = !mapData->m_isOfficial; } else { mapDisplayName.format(L"%hs", myGame->getMap().str()); - willTransfer = WouldMapTransfer(myGame->getMap()); } for( i = 0; i < MAX_SLOTS; i++ ) { @@ -370,6 +369,7 @@ void StartPressed() else TheLAN->RequestGameStart(); LANEnableStartButton(false); + return true; } else { @@ -381,6 +381,7 @@ void StartPressed() } } + return false; } void LANEnableStartButton(Bool enabled) @@ -856,10 +857,17 @@ 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 +1078,13 @@ void LanGameOptionsMenuShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void LanGameOptionsMenuUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + NetworkAutoStart::updateGameOptions(); + } +#endif + if(LANisShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout); //TheLAN->update(); // this is handled in the lobby @@ -1263,7 +1278,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/LanLobbyMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp index 143fb6b9852..aa04a2f97e6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanLobbyMenu.cpp @@ -60,6 +60,7 @@ #include "GameLogic/GameLogic.h" #include "GameNetwork/IPEnumeration.h" #include "GameNetwork/LANAPICallbacks.h" +#include "GameNetwork/NetworkAutoStart.h" #include "GameNetwork/LANGameInfo.h" Bool LANisShuttingDown = false; @@ -417,6 +418,12 @@ void LanLobbyMenuInit( WindowLayout *layout, void *userData ) // Choose an IP address, then initialize the LAN singleton UnsignedInt IP = TheGlobalData->m_defaultIP; +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && NetworkAutoStart::getLocalAddress() != 0) + { + IP = NetworkAutoStart::getLocalAddress(); + } +#endif IPEnumeration IPs; const WideChar* IPSource; if (!IP) @@ -428,6 +435,13 @@ void LanLobbyMenuInit( WindowLayout *layout, void *userData ) IPlist = IPlist->getNext(); } */ +#if defined(RTS_DEBUG) + if (!IPlist && NetworkAutoStart::isEnabled()) + { + NetworkAutoStart::onLocalAddressSet(false); + return; + } +#endif DEBUG_ASSERTCRASH(IPlist, ("No IP addresses found!")); if (!IPlist) { @@ -448,8 +462,14 @@ void LanLobbyMenuInit( WindowLayout *layout, void *userData ) #endif // TheLAN->init() sets us to be in a LAN menu screen automatically. - TheLAN->init(); - if (TheLAN->SetLocalIP(IP) == FALSE) { + if (TheLAN->init(IP) == FALSE) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled()) + { + NetworkAutoStart::onLocalAddressSet(false); + return; + } +#endif LANSocketErrorDetected = TRUE; } @@ -613,6 +633,25 @@ void LanLobbyMenuUpdate( WindowLayout * layout, void *userData) if (TheShell->isAnimFinished() && !LANbuttonPushed && TheLAN) TheLAN->update(); +#if defined(RTS_DEBUG) + if (NetworkAutoStart::hasFailed()) + { + return; + } + if (NetworkAutoStart::isEnabled() && LANSocketErrorDetected) + { + NetworkAutoStart::onLocalAddressSet(false); + return; + } + if (TheShell->isAnimFinished() && !LANbuttonPushed && NetworkAutoStart::shouldOpenDirectConnect()) + { + NetworkAutoStart::markDirectConnectOpened(); + TheWindowManager->winSendSystemMsg(buttonDirectConnect->winGetParent(), GBM_SELECTED, + (WindowMsgData)buttonDirectConnect, buttonDirectConnectID); + return; + } +#endif + if (LANSocketErrorDetected == TRUE) { LANSocketErrorDetected = FALSE; DEBUG_LOG(("SOCKET ERROR! BAILING!")); 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..5904d964cc0 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::shouldOpenLobby()) + { + NetworkAutoStart::markLobbyOpened(); + TheShell->push("Menus/LanLobbyMenu.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..e81c01fe008 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 ------------------------------------------------------------------------------ @@ -59,6 +60,7 @@ extern Bool LANbuttonPushed; extern Bool LANisShuttingDown; +extern Bool LANSocketErrorDetected; static Bool isShuttingDown = false; static Bool buttonPushed = false; @@ -252,12 +254,6 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) LANbuttonPushed = false; LANisShuttingDown = false; - if (TheLAN == nullptr) - { - TheLAN = NEW LANAPI(); - TheLAN->init(); - } - TheLAN->reset(); buttonPushed = false; isShuttingDown = false; @@ -296,6 +292,14 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) UnicodeString ipstr; + UnsignedInt autoLocalIP = 0; +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + autoLocalIP = TheLAN->GetLocalIP(); + } +#endif + delete TheLAN; TheLAN = nullptr; @@ -305,36 +309,48 @@ void NetworkDirectConnectInit( WindowLayout *layout, void *userData ) OptionPreferences prefs; UnsignedInt IP = prefs.getOnlineIPAddress(); + if (autoLocalIP != 0) + { + IP = autoLocalIP; + } - IPEnumeration IPs; + if (autoLocalIP == 0) + { + IPEnumeration IPs; -// if (!IP) -// { - EnumeratedIP *IPlist = IPs.getAddresses(); - DEBUG_ASSERTCRASH(IPlist, ("No IP addresses found!")); - if (!IPlist) - { - /// @todo: display error and exit lan lobby if no IPs are found - } + // if (!IP) + // { + EnumeratedIP *IPlist = IPs.getAddresses(); + DEBUG_ASSERTCRASH(IPlist, ("No IP addresses found!")); + if (!IPlist) + { + /// @todo: display error and exit lan lobby if no IPs are found + } - Bool foundIP = FALSE; - EnumeratedIP *tempIP = IPlist; - while ((tempIP != nullptr) && (foundIP == FALSE)) { - if (IP == tempIP->getIP()) { - foundIP = TRUE; + Bool foundIP = FALSE; + EnumeratedIP *tempIP = IPlist; + while ((tempIP != nullptr) && (foundIP == FALSE)) { + if (IP == tempIP->getIP()) { + foundIP = TRUE; + } + tempIP = tempIP->getNext(); } - tempIP = tempIP->getNext(); - } - if (foundIP == FALSE) { - // The IP that we had no longer exists, we need to pick a new one. - IP = IPlist->getIP(); - } + if (foundIP == FALSE) { + // The IP that we had no longer exists, we need to pick a new one. + IP = IPlist->getIP(); + } -// IP = IPlist->getIP(); -// } - TheLAN->init(); - TheLAN->SetLocalIP(IP); + // IP = IPlist->getIP(); + // } + } + if (!TheLAN->init(IP)) + { +#if defined(RTS_DEBUG) + NetworkAutoStart::onLocalAddressSet(false); +#endif + LANSocketErrorDetected = TRUE; + } } UnsignedInt ip = TheLAN->GetLocalIP(); @@ -393,6 +409,13 @@ void NetworkDirectConnectShutdown( WindowLayout *layout, void *userData ) //------------------------------------------------------------------------------------------------- void NetworkDirectConnectUpdate( WindowLayout * layout, void *userData) { +#if defined(RTS_DEBUG) + if (NetworkAutoStart::isEnabled() && TheLAN != nullptr) + { + NetworkAutoStart::updateDirectConnect(); + } +#endif + // We'll only be successful if we've requested to if(isShuttingDown && TheShell->isAnimFinished() && TheTransitionHandler->isFinished()) shutdownComplete(layout);