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);