Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Core/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameClient/MapUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameNetwork/LANAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
79 changes: 79 additions & 0 deletions Core/GameEngine/Include/GameNetwork/NetworkAutoStart.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#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
2 changes: 2 additions & 0 deletions Core/GameEngine/Include/GameNetwork/networkutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
130 changes: 130 additions & 0 deletions Core/GameEngine/Source/Common/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine

#include <limits.h>

#include "Common/ArchiveFileSystem.h"
#include "Common/CommandLine.h"
#include "Common/CRCDebug.h"
Expand All @@ -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"



Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions Core/GameEngine/Source/GameClient/MapUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnicodeString, rts::less_than_nocase<UnicodeString>/**/> MapNameList;
typedef std::map<UnicodeString, AsciiString> MapDisplayToFileNameList;
Expand Down
15 changes: 8 additions & 7 deletions Core/GameEngine/Source/GameNetwork/LANAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -135,6 +138,7 @@ void LANAPI::init()
m_hostName = "unknown";
}
#endif
return bound;
}

void LANAPI::reset()
Expand Down Expand Up @@ -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());
Expand Down
Loading
Loading