Skip to content

Conversation

@helmutbuhler
Copy link

This PR duplicates the changes in #651 for Generals.
Most changes were applied with the patchfile from the other PR:

git apply -p2 --directory=Generals --reject --whitespace=fix changes.patch

Some parts in these files could not be applied automatically and were duplicated manually:

Generals/Code/GameEngine/Source/Common/GameEngine.cpp.rej
Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp.rej
Generals/Code/GameEngine/Source/GameClient/GameClient.cpp.rej
Generals/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp.rej
Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp.rej
Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp.rej
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp.rej
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp.rej
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp.rej
Generals/Code/Main/WinMain.cpp.rej

The changes in BaseHeightMap.cpp were applied to HeightMap.cpp (the relevant code was moved between games)

@helmutbuhler
Copy link
Author

For reference, here is the patchfile that was used:

Details
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h b/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h
index 49581fb8..fea92a0e 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/GameAudio.h
@@ -372,6 +372,57 @@ class AudioManager : public SubsystemInterface
 		Bool m_disallowSpeech			: 1;
 };
 
+// TheSuperHackers @feature helmutbuhler 17/05/2025
+// AudioManager that does nothing. Used for Headless Mode.
+class AudioManagerDummy : public AudioManager
+{
+#if defined(RTS_DEBUG) || defined(RTS_INTERNAL)
+	virtual void audioDebugDisplay(DebugDisplayInterface* dd, void* userData, FILE* fp) {}
+#endif
+	virtual void stopAudio(AudioAffect which) {}
+	virtual void pauseAudio(AudioAffect which) {}
+	virtual void resumeAudio(AudioAffect which) {}
+	virtual void pauseAmbient(Bool shouldPause) {}
+	virtual void killAudioEventImmediately(AudioHandle audioEvent) {}
+	virtual void nextMusicTrack() {}
+	virtual void prevMusicTrack() {}
+	virtual Bool isMusicPlaying() const { return false; }
+	virtual Bool hasMusicTrackCompleted(const AsciiString& trackName, Int numberOfTimes) const { return false; }
+	virtual AsciiString getMusicTrackName() const { return ""; }
+	virtual void openDevice() {}
+	virtual void closeDevice() {}
+	virtual void* getDevice() { return NULL; }
+	virtual void notifyOfAudioCompletion(UnsignedInt audioCompleted, UnsignedInt flags) {}
+	virtual UnsignedInt getProviderCount(void) const { return 0; };
+	virtual AsciiString getProviderName(UnsignedInt providerNum) const { return ""; }
+	virtual UnsignedInt getProviderIndex(AsciiString providerName) const { return 0; }
+	virtual void selectProvider(UnsignedInt providerNdx) {}
+	virtual void unselectProvider(void) {}
+	virtual UnsignedInt getSelectedProvider(void) const { return 0; }
+	virtual void setSpeakerType(UnsignedInt speakerType) {}
+	virtual UnsignedInt getSpeakerType(void) { return 0; }
+	virtual UnsignedInt getNum2DSamples(void) const { return 0; }
+	virtual UnsignedInt getNum3DSamples(void) const { return 0; }
+	virtual UnsignedInt getNumStreams(void) const { return 0; }
+	virtual Bool doesViolateLimit(AudioEventRTS* event) const { return false; }
+	virtual Bool isPlayingLowerPriority(AudioEventRTS* event) const { return false; }
+	virtual Bool isPlayingAlready(AudioEventRTS* event) const { return false; }
+	virtual Bool isObjectPlayingVoice(UnsignedInt objID) const { return false; }
+	virtual void adjustVolumeOfPlayingAudio(AsciiString eventName, Real newVolume) {}
+	virtual void removePlayingAudio(AsciiString eventName) {}
+	virtual void removeAllDisabledAudio() {}
+	virtual Bool has3DSensitiveStreamsPlaying(void) const { return false; }
+	virtual void* getHandleForBink(void) { return NULL; }
+	virtual void releaseHandleForBink(void) {}
+	virtual void friend_forcePlayAudioEventRTS(const AudioEventRTS* eventToPlay) {}
+	virtual void setPreferredProvider(AsciiString providerNdx) {}
+	virtual void setPreferredSpeaker(AsciiString speakerType) {}
+	virtual Real getFileLengthMS(AsciiString strToLoad) const { return -1; }
+	virtual void closeAnySamplesUsingFile(const void* fileToClose) {}
+	virtual void setDeviceListenerPosition(void) {}
+};
+
+
 extern AudioManager *TheAudio;
 
 #endif // __COMMON_GAMEAUDIO_H_
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
index 73e3eadb..5c7957e8 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
@@ -100,6 +100,11 @@ public:
 	Bool m_dumpAssetUsage;
 	Int m_framesPerSecondLimit;
 	Int	m_chipSetType;	///<See W3DShaderManager::ChipsetType for options
+
+	// TheSuperHackers @feature helmutbuhler 11/04/2025
+	// Run game without graphics, input or audio.
+	Bool m_headless;
+	
 	Bool m_windowed;
 	Int m_xResolution;
 	Int m_yResolution;
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h
index 98da900d..6f40897b 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/Radar.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/Radar.h
@@ -293,6 +293,16 @@ protected:
 // EXTERNALS //////////////////////////////////////////////////////////////////////////////////////
 extern Radar *TheRadar;  ///< the radar singleton extern
 
+// TheSuperHackers @feature helmutbuhler 10/04/2025
+// Radar that does nothing. Used for Headless Mode.
+class RadarDummy : public Radar
+{
+public:
+	virtual void draw(Int pixelX, Int pixelY, Int width, Int height) { }
+	virtual void clearShroud() { }
+	virtual void setShroudLevel(Int x, Int y, CellShroudStatus setting) { }
+};
+
 #endif  // __RADAR_H_
 
 
diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h
index 78edbf64..3411ab8d 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h
@@ -259,4 +259,47 @@ inline Drawable* GameClient::findDrawableByID( const DrawableID id )
 // the singleton
 extern GameClient *TheGameClient;
 
+
+// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+// Some information about the architecture and headless mode:
+// The game is structurally separated into GameLogic and GameClient.
+// The Logic is responsible for everything that affects the game mechanic and what is synchronized over
+// the network. The Client is responsible for rendering, input, audio and similar stuff.
+// 
+// Unfortunately there are some places in the code that make the Logic depend on the Client.
+// (Search for @logic-client-separation)
+// That means if we want to run the game headless, we cannot just disable the Client. We need to disable
+// the parts in the Client that don't work in headless mode and need to keep the parts that are needed
+// to run the Logic.
+// The following describes which parts we disable in headless mode:
+//
+//	GameEngine:
+//		TheGameClient is partially disabled:
+//			TheKeyboard = NULL
+//			TheMouse = NULL
+//			TheDisplay is partially disabled:
+//				m_3DInterfaceScene = NULL
+//				m_2DScene = NULL
+//				m_3DScene = NULL
+//				(m_assetManager remains!)
+//			TheWindowManager = GameWindowManagerDummy
+//			TheIMEManager = NULL
+//			TheTerrainVisual is partially disabled:
+//				TheTerrainTracksRenderObjClassSystem = NULL
+//				TheW3DShadowManager = NULL
+//				TheWaterRenderObj = NULL
+//				TheSmudgeManager = NULL
+//				TheTerrainRenderObject is partially disabled:
+//					m_treeBuffer = NULL
+//					m_propBuffer = NULL
+//					m_bibBuffer = NULL
+//					m_bridgeBuffer is partially disabled:
+//						m_vertexBridge = NULL
+//						m_indexBridge = NULL
+//						m_vertexMaterial = NULL
+//					m_waypointBuffer = NULL
+//					m_roadBuffer = NULL
+//					m_shroud = NULL
+//		TheRadar = RadarDummy
+
 #endif // _GAME_INTERFACE_H_
diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindow.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindow.h
index 98fa9710..c8e6b738 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindow.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindow.h
@@ -322,7 +322,7 @@ public:
 	// window instance data
 	Int winSetInstanceData( WinInstanceData *data );  ///< copy over instance data
 	WinInstanceData *winGetInstanceData( void );  ///< get instance data
-	void *winGetUserData( void );  ///< get the window user data
+	virtual void *winGetUserData( void );  ///< get the window user data
 	void winSetUserData( void *userData );  ///< set the user data
 
 	// heirarchy methods
@@ -426,6 +426,16 @@ protected:
 
 };  // end class GameWindow
 
+// TheSuperHackers @feature helmutbuhler 24/04/2025
+// GameWindow that does nothing. Used for Headless Mode.
+class GameWindowDummy : public GameWindow
+{
+	MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(GameWindowDummy, "GameWindowDummy")
+public:
+	virtual void winDrawBorder() {}
+	virtual void* winGetUserData(void) { return NULL; }
+};
+
 // ModalWindow ----------------------------------------------------------------
 //-----------------------------------------------------------------------------
 class ModalWindow : public MemoryPoolObject
diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h
index 7965895b..fda79052 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h
@@ -379,7 +379,39 @@ extern WindowMsgHandledType PassMessagesToParentSystem( GameWindow *window,
 																															 WindowMsgData mData1, 
 																															 WindowMsgData mData2 );
 
-
+// TheSuperHackers @feature helmutbuhler 24/04/2025
+// GameWindowManager that does nothing. Used for Headless Mode.
+class GameWindowManagerDummy : public GameWindowManager
+{
+public:
+	virtual GameWindow *winGetWindowFromId(GameWindow *window, Int id);
+	virtual GameWindow *winCreateFromScript(AsciiString filenameString, WindowLayoutInfo *info);
+
+	virtual GameWindow *allocateNewWindow() { return newInstance(GameWindowDummy); }
+
+	virtual GameWinDrawFunc getPushButtonImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getPushButtonDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getCheckBoxImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getCheckBoxDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getRadioButtonImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getRadioButtonDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getTabControlImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getTabControlDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getListBoxImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getListBoxDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getComboBoxImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getComboBoxDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getHorizontalSliderImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getHorizontalSliderDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getVerticalSliderImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getVerticalSliderDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getProgressBarImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getProgressBarDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getStaticTextImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getStaticTextDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getTextEntryImageDrawFunc() { return NULL; }
+	virtual GameWinDrawFunc getTextEntryDrawFunc() { return NULL; }
+};
 
 #endif // __GAMEWINDOWMANAGER_H_
 
diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Mouse.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Mouse.h
index 64cebc79..1b886c5f 100644
--- a/GeneralsMD/Code/GameEngine/Include/GameClient/Mouse.h
+++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Mouse.h
@@ -371,7 +371,20 @@ protected:
 
 };  // end class Mouse
 
-// INLINING ///////////////////////////////////////////////////////////////////
+// TheSuperHackers @feature helmutbuhler 17/05/2025
+// Mouse that does nothing. Used for Headless Mode.
+class MouseDummy : public Mouse
+{
+	virtual void parseIni() {}
+	virtual void update() {}
+	virtual void initCursorResources() {}
+	virtual void createStreamMessages() {}
+	virtual void setCursor(MouseCursor cursor) {}
+	virtual void capture() {}
+	virtual void releaseCapture() {}
+	virtual UnsignedByte getMouseEvent(MouseIO *result, Bool flush) { return MOUSE_NONE; }
+};
+
 
 // EXTERNALS //////////////////////////////////////////////////////////////////
 extern Mouse *TheMouse;  ///< extern mouse singleton definition
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp
index 14fd93a2..f5899829 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp
@@ -862,6 +862,15 @@ Int parseQuickStart( char *args[], int num )
 	return 1;
 }
 
+Int parseHeadless( char *args[], int num )
+{
+	if (TheWritableGlobalData)
+	{
+		TheWritableGlobalData->m_headless = TRUE;
+	}
+	return 1;
+}
+
 Int parseConstantDebug( char *args[], int num )
 {
 	if (TheWritableGlobalData)
@@ -1206,6 +1215,10 @@ static CommandLineParam params[] =
 	{ "-quickstart", parseQuickStart },
 	{ "-useWaveEditor", parseUseWaveEditor },
 
+	// TheSuperHackers @feature helmutbuhler 11/04/2025
+	// This runs the game without a window, graphics, input and audio. Used for testing.
+	{ "-headless", parseHeadless },
+
 #if (defined(RTS_DEBUG) || defined(RTS_INTERNAL))
 	{ "-noaudio", parseNoAudio },
 	{ "-map", parseMapName },
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
index 9e79d399..3a23c83a 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
@@ -431,7 +431,7 @@ void GameEngine::init( int argc, char *argv[] )
   startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
 	DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
 	#endif/////////////////////////////////////////////////////////////////////////////////////////////
-		initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL);
+		initSubsystem(TheAudio,"TheAudio", TheGlobalData->m_headless ? NEW AudioManagerDummy : createAudioManager(), NULL);
 		if (!TheAudio->isMusicAlreadyLoaded())
 			setQuitting(TRUE);
 
@@ -507,7 +507,7 @@ void GameEngine::init( int argc, char *argv[] )
 		initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini");
 		initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL);
 		initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL);
-		initSubsystem(TheRadar,"TheRadar", createRadar(), NULL);
+		initSubsystem(TheRadar,"TheRadar", TheGlobalData->m_headless ? NEW RadarDummy : createRadar(), NULL);
 		initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL);
 
 
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
index 21875cda..bf4bc3d5 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
@@ -613,6 +613,7 @@ GlobalData::GlobalData()
 	m_dumpAssetUsage = FALSE;
 	m_framesPerSecondLimit = 0;
 	m_chipSetType = 0;
+	m_headless = FALSE;
 	m_windowed = 0;
 	m_xResolution = 800;
 	m_yResolution = 600;
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp
index eb5f09c6..5fa23da0 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/RandomValue.cpp
@@ -249,7 +249,7 @@ Int GetGameClientRandomValue( int lo, int hi, const char *file, int line )
 /**/
 #ifdef DEBUG_RANDOM_CLIENT
 DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n",
-				TheGameLogic->getFrame(), rval, lo, hi, file, line ));
+				TheGameLogic ? TheGameLogic->getFrame() : -1, rval, lo, hi, file, line ));
 #endif
 /**/
 
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp
index b8ebaa84..5c377d6f 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/System/MemoryInit.cpp
@@ -416,6 +416,7 @@ static PoolSizeRec sizes[] =
 	{ "Overridable", 32, 32 },
 
 	{ "W3DGameWindow", 700, 256 },
+	{ "GameWindowDummy", 700, 256 },
 	{ "SuccessState", 32, 32 },
 	{ "FailureState", 32, 32 },
 	{ "ContinueState", 32, 32 },
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
index 663491e0..56e8a6d5 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp
@@ -1382,6 +1382,8 @@ void ControlBar::reset( void )
 //-------------------------------------------------------------------------------------------------
 void ControlBar::update( void )
 {
+	if (TheGlobalData->m_headless)
+		return;
 	getStarImage();
 	updateRadarAttackGlow();
 	if(m_controlBarSchemeManager)
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp
index 17d9b656..a1aeefb4 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp
@@ -2221,6 +2221,8 @@ Int GadgetListBoxAddEntryText( GameWindow *listbox,
 	addInfo.width = -1;
 
 	ListboxData *listData = (ListboxData *)listbox->winGetUserData();
+	if (listData == NULL)
+		return -1;
 	Bool wasFull = (listData->listLength <= listData->endPos);
 	Int newEntryOffset = (wasFull)?0:1;
 	Int oldBottomIndex = GadgetListBoxGetBottomVisibleEntry(listbox);
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp
index 4be961bc..e2c9a84b 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp
@@ -4097,3 +4097,36 @@ void GameWindowManager::clearTabList( void )
 {
 	m_tabList.clear();
 }
+
+
+GameWindow *GameWindowManagerDummy::winGetWindowFromId(GameWindow *window, Int id)
+{
+	window = GameWindowManager::winGetWindowFromId(window, id);
+	if (window != NULL)
+		return window;
+
+	// Just return any window, callers expect this to be non-null
+	return m_windowList;
+}
+
+WindowMsgHandledType DummyWindowSystem(GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2)
+{
+	return MSG_IGNORED;
+}
+
+GameWindow *GameWindowManagerDummy::winCreateFromScript(AsciiString filenameString, WindowLayoutInfo *info)
+{
+	WindowLayoutInfo scriptInfo;
+	GameWindow* dummyWindow = winCreate(NULL, 0, 0, 0, 100, 100, DummyWindowSystem, NULL);
+	scriptInfo.windows.push_back(dummyWindow);
+	if (info)
+		*info = scriptInfo;
+	return dummyWindow;
+}
+
+GameWindowDummy::~GameWindowDummy()
+{
+}
+
+
+
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp
index ee53c277..b7b15cce 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp
@@ -270,10 +270,13 @@ void GameClient::init( void )
 		TheDisplayStringManager->setName("TheDisplayStringManager");
 	}
 	
-	// create the keyboard
-	TheKeyboard = createKeyboard();
-	TheKeyboard->init();
-	TheKeyboard->setName("TheKeyboard");
+	if (!TheGlobalData->m_headless)
+	{
+		// create the keyboard
+		TheKeyboard = createKeyboard();
+		TheKeyboard->init();
+		TheKeyboard->setName("TheKeyboard");
+	}
 
 	// allocate and load image collection for the GUI and just load the 256x256 ones for now
 	TheMappedImageCollection = MSGNEW("GameClientSubsystem") ImageCollection;
@@ -322,7 +325,7 @@ void GameClient::init( void )
 		TheFontLibrary->init();
 
 	// create the mouse
-	TheMouse = createMouse();
+	TheMouse = TheGlobalData->m_headless ? NEW MouseDummy : createMouse();
 	TheMouse->parseIni();
 	TheMouse->initCursorResources();
  	TheMouse->setName("TheMouse");
@@ -340,7 +343,7 @@ void GameClient::init( void )
 	}
 
 	// create the window manager
-	TheWindowManager = createWindowManager();
+	TheWindowManager = TheGlobalData->m_headless ? NEW GameWindowManagerDummy : createWindowManager();
 	if( TheWindowManager )
 	{
 
@@ -535,7 +538,7 @@ void GameClient::update( void )
 	//Initial Game Codition.  We must show the movie first and then we can display the shell	
 	if(TheGlobalData->m_afterIntro && !TheDisplay->isMoviePlaying())
 	{
-		if( playSizzle && TheGlobalData->m_playSizzle )
+		if( playSizzle && TheGlobalData->m_playSizzle && !TheGlobalData->m_headless )// Remove headless-check with Replay Simulation PR
 		{
 			TheWritableGlobalData->m_allowExitOutOfMovies = TRUE;
 			if(TheGameLODManager && TheGameLODManager->didMemPass())
@@ -626,10 +629,9 @@ void GameClient::update( void )
 	if(TheGlobalData->m_playIntro || TheGlobalData->m_afterIntro)
 	{
 		// redraw all views, update the GUI
+		if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR
 		{
 			TheDisplay->DRAW();
-		}
-		{
 			TheDisplay->UPDATE();
 		}
 		return;
@@ -749,10 +751,12 @@ void GameClient::update( void )
 	}
 
 	// update display
+	if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR
 	{
 		TheDisplay->UPDATE();
 	}
 
+	if (!TheGlobalData->m_headless)// Remove headless-check with Replay Simulation PR
 	{
 		USE_PERF_TIMER(GameClient_draw)
 			
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
index 27f96ef6..fabf6fa4 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
@@ -2218,6 +2218,8 @@ const WaterHandle* TerrainLogic::getWaterHandle( Real x, Real y )
 
 	/**@todo: Remove this after we have all water types included
 		in water triggers.  For now do special check for water grid mesh. */
+	// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+	// We shouldn't depend on TerrainVisual here.
 	Real meshZ;
 	if( TheTerrainVisual->getWaterGridHeight( x, y, &meshZ ) )
 	{	
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
index 71b18c5d..540cdfef 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
@@ -6094,6 +6094,9 @@ const AsciiString& Object::getCommandSetString() const
 //=============================================================================
 Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade )
 {
+	// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+	// TheControlBar belongs to the client, we shouldn't depend on that to check this.
+
 	// We need to have the button to make the upgrade.  CommandSets are a weird Logic/Client hybrid.
 	const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString());
 
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp
index 75a770a2..4478c038 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp
@@ -510,6 +510,10 @@ void DockUpdate::loadDockPositions()
 			if( m_numberApproachPositions != DYNAMIC_APPROACH_VECTOR_FLAG )
 			{
 				// Dynamic means no bones
+
+				// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+				// We shouldn't depend on bones of a drawable here!
+
 				// TheSuperHackers @fix helmutbuhler 19/04/2025 Zero initialize array to prevent uninitialized memory reads.
 				// Important: the entire target vector is used for serialization and crc and must not contain random data.
 				Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE] = {0};
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp
index fddde986..97b0f03a 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp
@@ -2598,6 +2598,8 @@ Bool Weapon::privateFireWeapon(
 	Bool reloaded = false;
 	if (m_ammoInClip > 0)
 	{
+		// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+		// barrelCount shouln't depend on Drawable, which belongs to client.
 		Int barrelCount = sourceObj->getDrawable()->getBarrelCount(m_wslot);
 		if (m_curBarrel >= barrelCount)
 		{
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
index 74718ca7..c19b42f9 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
@@ -736,6 +736,9 @@ static void populateRandomSideAndColor( GameInfo *game )
 		// Prevent from selecting the disabled Generals.
 		// This is also enforced at GUI setup (GUIUtil.cpp).
 		// @todo: unlock these when something rad happens
+
+		// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+		// TheChallengeGenerals belongs to client, we shouldn't depend on that here.
 		Bool disallowLockedGenerals = TRUE;
 		const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName());
 		Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;
@@ -1266,7 +1269,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame )
 	//****************************//
 
 	// Get the m_loadScreen for this kind of game
-	if(!m_loadScreen)
+	if(!m_loadScreen && !TheGlobalData->m_headless)
 	{
 		m_loadScreen = getLoadScreen( loadingSaveGame );
 		if(m_loadScreen)
@@ -1981,6 +1984,9 @@ void GameLogic::startNewGame( Bool loadingSaveGame )
 				// The game will start, but the cheater will be instantly defeated because he has no troops.
 				// This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp).
 				// @todo: unlock these when something rad happens
+				
+				// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+				// TheChallengeGenerals belongs to client, we shouldn't depend on that here.
 				Bool disallowLockedGenerals = TRUE;
 				const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName());
 				Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;
@@ -2186,7 +2192,7 @@ void GameLogic::startNewGame( Bool loadingSaveGame )
 	}
 
 	// if we're in a load game, don't fade yet
-	if( loadingSaveGame == FALSE )
+	if(loadingSaveGame == FALSE && TheTransitionHandler != NULL)
 	{
 		TheTransitionHandler->setGroup("FadeWholeScreen");
 		while(!TheTransitionHandler->isFinished())
@@ -2226,14 +2232,17 @@ void GameLogic::startNewGame( Bool loadingSaveGame )
 
 	if(m_gameMode == GAME_SHELL)
 	{
-		if(TheShell->getScreenCount() == 0)
-			TheShell->push( AsciiString("Menus/MainMenu.wnd") );
-		else if (TheShell->top())
+		if (!TheGlobalData->m_headless)
 		{
-			TheShell->top()->hide(FALSE);
-			TheShell->top()->bringForward();
+			if(TheShell->getScreenCount() == 0)
+				TheShell->push( AsciiString("Menus/MainMenu.wnd") );
+			else if (TheShell->top())
+			{
+				TheShell->top()->hide(FALSE);
+				TheShell->top()->bringForward();
+			}
+			HideControlBar();
 		}
-		HideControlBar();
 	}
 	else
 	{
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
index 18d7ab1c..c3430b2a 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
@@ -265,7 +265,7 @@ void GameLogic::clearGameData( Bool showScoreScreen )
 	TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows.
 
 	Bool shellGame = FALSE;
-	if ((!isInShellGame() || !isInGame()) && showScoreScreen)
+	if ((!isInShellGame() || !isInGame()) && showScoreScreen && !TheGlobalData->m_headless)
 	{
 		shellGame = TRUE;
 		TheTransitionHandler->setGroup("FadeWholeScreen");
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp
index ba8add5e..bc7601a2 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp
@@ -288,36 +288,41 @@ BaseHeightMapRenderObjClass::BaseHeightMapRenderObjClass(void)
 	m_useDepthFade = false;
 	m_disableTextures = false;
 	TheTerrainRenderObject = this;
-	m_treeBuffer = NULL; 
-
-	m_treeBuffer = NEW W3DTreeBuffer;
 
+	m_treeBuffer = NULL; 
 	m_propBuffer = NULL; 
-
-	m_propBuffer = NEW W3DPropBuffer;
-
-
 	m_bibBuffer = NULL;
-	m_bibBuffer = NEW W3DBibBuffer;
-	m_curImpassableSlope = 45.0f;	// default to 45 degrees.
 	m_bridgeBuffer = NULL;
-	m_bridgeBuffer = NEW W3DBridgeBuffer;
-	m_waypointBuffer = NEW W3DWaypointBuffer;
+	m_waypointBuffer = NULL;
 #ifdef DO_ROADS
 	m_roadBuffer = NULL;
-	m_roadBuffer = NEW W3DRoadBuffer;
 #endif
 #ifdef DO_SCORCH
 	m_vertexScorch = NULL;
 	m_indexScorch = NULL;
 	m_scorchTexture = NULL;
 	clearAllScorches();
+	m_shroud = NULL;
+#endif
+	m_bridgeBuffer = NEW W3DBridgeBuffer;
+
+	if (TheGlobalData->m_headless)
+		return;
+
+	m_treeBuffer = NEW W3DTreeBuffer;
+
+	m_propBuffer = NEW W3DPropBuffer;
+
+	m_bibBuffer = NEW W3DBibBuffer;
+
+	m_curImpassableSlope = 45.0f;	// default to 45 degrees.
+	m_waypointBuffer = NEW W3DWaypointBuffer;
+#ifdef DO_ROADS
+	m_roadBuffer = NEW W3DRoadBuffer;
 #endif
 #if defined(RTS_DEBUG) || defined(RTS_INTERNAL)
 	if (TheGlobalData->m_shroudOn)
 		m_shroud = NEW W3DShroud;
-	else
-		m_shroud = NULL;
 #else
 	m_shroud = NEW W3DShroud;
 #endif
@@ -1791,7 +1796,8 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM
 	if (m_shroud)
 		m_shroud->init(m_map,TheGlobalData->m_partitionCellSize,TheGlobalData->m_partitionCellSize);
 #ifdef DO_ROADS
-	m_roadBuffer->setMap(m_map);
+	if (m_roadBuffer)
+		m_roadBuffer->setMap(m_map);
 #endif
 	HeightSampleType *data = NULL;
 	if (pMap) {
@@ -1846,7 +1852,7 @@ Int BaseHeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pM
 	m_curNumScorchIndices=0;
 	// If the textures aren't allocated (usually because of a hardware reset) need to allocate.
 	Bool needToAllocate = false;
-	if (m_stageTwoTexture == NULL) {
+	if (m_stageTwoTexture == NULL && m_treeBuffer) {
 		needToAllocate = true;
 	}
 	if (data && needToAllocate)
@@ -2300,7 +2306,8 @@ void BaseHeightMapRenderObjClass::notifyShroudChanged(void)
 void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4], 
 																						ObjectID id, Bool highlight)
 {
-	m_bibBuffer->addBib(corners, id, highlight); 
+	if (m_bibBuffer)
+		m_bibBuffer->addBib(corners, id, highlight); 
 };
 
 //=============================================================================
@@ -2311,7 +2318,8 @@ void BaseHeightMapRenderObjClass::addTerrainBib(Vector3 corners[4],
 void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4], 
 																						DrawableID id, Bool highlight)
 {
-	m_bibBuffer->addBibDrawable(corners, id, highlight); 
+	if (m_bibBuffer)
+		m_bibBuffer->addBibDrawable(corners, id, highlight); 
 };
 
 //=============================================================================
@@ -2321,7 +2329,8 @@ void BaseHeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4],
 //=============================================================================
 void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting()
 {
-	m_bibBuffer->removeHighlighting(  ); 
+	if (m_bibBuffer)
+		m_bibBuffer->removeHighlighting(  ); 
 };
 
 //=============================================================================
@@ -2331,7 +2340,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBibHighlighting()
 //=============================================================================
 void BaseHeightMapRenderObjClass::removeAllTerrainBibs()
 {
-	m_bibBuffer->clearAllBibs(  ); 
+	if (m_bibBuffer)
+		m_bibBuffer->clearAllBibs(  ); 
 };
 
 //=============================================================================
@@ -2341,7 +2351,8 @@ void BaseHeightMapRenderObjClass::removeAllTerrainBibs()
 //=============================================================================
 void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id)
 {
-	m_bibBuffer->removeBib( id ); 
+	if (m_bibBuffer)
+		m_bibBuffer->removeBib( id ); 
 };
 
 //=============================================================================
@@ -2351,7 +2362,8 @@ void BaseHeightMapRenderObjClass::removeTerrainBib(ObjectID id)
 //=============================================================================
 void BaseHeightMapRenderObjClass::removeTerrainBibDrawable(DrawableID id)
 {
-	m_bibBuffer->removeBibDrawable( id ); 
+	if (m_bibBuffer)
+		m_bibBuffer->removeBibDrawable( id ); 
 };
 
 //=============================================================================
@@ -2368,7 +2380,8 @@ void BaseHeightMapRenderObjClass::staticLightingChanged( void )
 	m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer.
 	m_curNumScorchVertices=0;
 	m_curNumScorchIndices=0;
-	m_roadBuffer->updateLighting();
+	if (m_roadBuffer)
+		m_roadBuffer->updateLighting();
 
 }
 
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp
index 1e709a3c..f4fca38d 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp
@@ -73,7 +73,8 @@ W3DDebrisDraw::~W3DDebrisDraw(void)
 	}
 	if (m_renderObject)
 	{
-		W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
+		if (W3DDisplay::m_3DScene != NULL)
+			W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
   	REF_PTR_RELEASE(m_renderObject);
  		m_renderObject = NULL; 	
 	}
@@ -111,7 +112,8 @@ void W3DDebrisDraw::setModelName(AsciiString name, Color color, ShadowType t)
 		DEBUG_ASSERTCRASH(m_renderObject, ("Debris model %s not found!\n",name.str()));
 		if (m_renderObject)
 		{
-			W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
+			if (W3DDisplay::m_3DScene != NULL)
+				W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
 
 			m_renderObject->Set_User_Data(getDrawable()->getDrawableInfo());
 			
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp
index 31ef5c65..d062579a 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp
@@ -205,7 +205,8 @@ W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) :
 				}
 
 				// add to scene
-				W3DDisplay::m_3DScene->Add_Render_Object( line );	//add it to our scene so it gets rendered with other objects.
+				if (W3DDisplay::m_3DScene != NULL)
+					W3DDisplay::m_3DScene->Add_Render_Object( line );	//add it to our scene so it gets rendered with other objects.
 
 				// hide the render object until the first time we come to draw it and
 				// set the correct position
@@ -229,7 +230,8 @@ W3DLaserDraw::~W3DLaserDraw( void )
 	{
 
 		// remove line from scene
-		W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
+		if (W3DDisplay::m_3DScene != NULL)
+			W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
 
 		// delete line
 		REF_PTR_RELEASE( m_line3D[ i ] );
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
index 54e6f71f..3cf52348 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
@@ -2808,7 +2808,8 @@ void W3DModelDraw::nukeCurrentRender(Matrix3D* xform)
 		// save the transform for the new model
 		if (xform)
 			*xform = m_renderObject->Get_Transform();
-		W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
+		if (W3DDisplay::m_3DScene != NULL)
+			W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
 		REF_PTR_RELEASE(m_renderObject);
 		m_renderObject = NULL;
 	}
@@ -3135,7 +3136,8 @@ void W3DModelDraw::setModelState(const ModelConditionInfo* newState)
    		}
 
 			// add render object to our scene
-			W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
+			if (W3DDisplay::m_3DScene != NULL)
+				W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
 
 			// tie in our drawable as the user data pointer in the render object
 			m_renderObject->Set_User_Data(draw->getDrawableInfo());
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp
index 50d3ba8e..1cb9aebe 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp
@@ -107,8 +107,11 @@ void W3DRopeDraw::buildSegments()
 																	 m_color.blue,  // blue
 																	 0.5f );  // transparency
 
-		W3DDisplay::m_3DScene->Add_Render_Object( info.line );
-		W3DDisplay::m_3DScene->Add_Render_Object( info.softLine );
+		if (W3DDisplay::m_3DScene)
+		{
+			W3DDisplay::m_3DScene->Add_Render_Object( info.line );
+			W3DDisplay::m_3DScene->Add_Render_Object( info.softLine );
+		}
 		m_segments.push_back(info);
 	}
 }
@@ -122,12 +125,14 @@ void W3DRopeDraw::tossSegments()
 	{
 		if (it->line)
 		{
-			W3DDisplay::m_3DScene->Remove_Render_Object(it->line);
+			if (W3DDisplay::m_3DScene)
+				W3DDisplay::m_3DScene->Remove_Render_Object(it->line);
 			REF_PTR_RELEASE((it->line));
 		}
 		if (it->softLine)
 		{
-			W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine);
+			if (W3DDisplay::m_3DScene)
+				W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine);
 			REF_PTR_RELEASE((it->softLine));
 		}
 	}
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp
index 95bd772b..16f5ece8 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp
@@ -1026,7 +1026,7 @@ Int HeightMapRenderObjClass::updateBlock(Int x0, Int y0, Int x1, Int y1,  WorldH
 	DEBUG_ASSERTCRASH(y0<=y1, ("HeightMapRenderObjClass::UpdateBlock parameters have inside-out rectangle (on Y)."));
 #endif
 	Invalidate_Cached_Bounding_Volumes();
-	if (pMap) {
+	if (pMap && m_treeBuffer != NULL) {
 		REF_PTR_SET(m_stageZeroTexture, pMap->getTerrainTexture());
 		REF_PTR_SET(m_stageOneTexture, pMap->getAlphaTerrainTexture());
 	}
@@ -1326,7 +1326,7 @@ Int HeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pMap,
 	if (m_stageOneTexture == NULL) {
 		needToAllocate = true;
 	}
-	if (data && needToAllocate)
+	if (data && needToAllocate && m_treeBuffer != NULL)
 	{	//requested heightmap different from old one.
 		freeIndexVertexBuffers();
 		//Create static index buffers.  These will index the vertex buffers holding the map.
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp
index 01a50382..ef969e20 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp
@@ -855,6 +855,9 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj(
 */
 int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color)
 {
+	if (TheGlobalData->m_headless)
+		return 0;
+
 	switch (robj->Class_ID())	{	
 	case RenderObjClass::CLASSID_MESH:
 		return Recolor_Mesh(robj,color);
@@ -871,6 +874,9 @@ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color)
 */
 int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color)
 {
+	if (TheGlobalData->m_headless)
+		return 0;
+
 	int i;
 	int didRecolor=0;
 	const char *meshName;
@@ -916,6 +922,9 @@ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color)
 
 int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color)
 {
+	if (TheGlobalData->m_headless)
+		return 0;
+
 	int didRecolor=0;
 
 	int num_sub = robj->Get_Num_Sub_Objects();
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp
index df141f96..aaf0378b 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp
@@ -768,6 +768,8 @@ void W3DBridgeBuffer::freeBridgeBuffers(void)
 //=============================================================================
 void W3DBridgeBuffer::allocateBridgeBuffers(void)
 {
+	if (TheGlobalData->m_headless)
+		return;
 	m_vertexBridge=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZNDUV1,MAX_BRIDGE_VERTEX+4,DX8VertexBufferClass::USAGE_DYNAMIC));
 	m_indexBridge=NEW_REF(DX8IndexBufferClass,(MAX_BRIDGE_INDEX+4, DX8IndexBufferClass::USAGE_DYNAMIC));
 	m_vertexMaterial=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
@@ -808,6 +810,8 @@ void W3DBridgeBuffer::clearAllBridges(void)
 //=============================================================================
 void W3DBridgeBuffer::loadBridges(W3DTerrainLogic *pTerrainLogic, Bool saveGame)
 {
+	// TheSuperHackers @logic-client-separation helmutbuhler 26/4/2025
+	// W3DBridgeBuffer shouldn't add objects to W3DTerrainLogic
 	clearAllBridges();
 	MapObject *pMapObj;
 	MapObject *pMapObj2;
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
index 7b5a8bdd..39cf955e 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
@@ -464,12 +464,15 @@ W3DDisplay::~W3DDisplay()
 
 	// shutdown
 	Debug_Statistics::Shutdown_Statistics();
-	W3DShaderManager::shutdown();
+	if (!TheGlobalData->m_headless)
+		W3DShaderManager::shutdown();
 	m_assetManager->Free_Assets();
 	delete m_assetManager;
-	WW3D::Shutdown();
+	if (!TheGlobalData->m_headless)
+		WW3D::Shutdown();
 	WWMath::Shutdown();
-	DX8WebBrowser::Shutdown();
+	if (!TheGlobalData->m_headless)
+		DX8WebBrowser::Shutdown();
 	delete TheW3DFileSystem;
 	TheW3DFileSystem = NULL;
 
@@ -682,49 +685,54 @@ void W3DDisplay::init( void )
 	// init the Westwood math library
 	WWMath::Init();
 
-	// create our 3D interface scene
-	m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () );
-	m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
+	if (!TheGlobalData->m_headless)
+	{
 
-	// create our 2D scene
-	m_2DScene = NEW_REF( RTS2DScene, () );
-	m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
+		// create our 3D interface scene
+		m_3DInterfaceScene = NEW_REF( RTS3DInterfaceScene, () );
+		m_3DInterfaceScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
 
-	// create our 3D scene
-	m_3DScene =NEW_REF( RTS3DScene, () );
-#if defined(RTS_DEBUG) || defined(RTS_INTERNAL)
-	if( TheGlobalData->m_wireframe )
-		m_3DScene->Set_Polygon_Mode( SceneClass::LINE );
-#endif
-//============================================================================
-	// m_myLight = NEW_REF
-//============================================================================
-	Int lindex;
-	for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++) 
-	{	m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) );
-	}
+		// create our 2D scene
+		m_2DScene = NEW_REF( RTS2DScene, () );
+		m_2DScene->Set_Ambient_Light( Vector3( 1, 1, 1 ) );
+
+		// create our 3D scene
+		m_3DScene =NEW_REF( RTS3DScene, () );
+	#if defined(RTS_DEBUG) || defined(RTS_INTERNAL)
+		if( TheGlobalData->m_wireframe )
+			m_3DScene->Set_Polygon_Mode( SceneClass::LINE );
+	#endif
+	//============================================================================
+		// m_myLight = NEW_REF
+	//============================================================================
+		Int lindex;
+		for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++) 
+		{	m_myLight[lindex] = NEW_REF( LightClass, (LightClass::DIRECTIONAL) );
+		}
 
-	setTimeOfDay( TheGlobalData->m_timeOfDay );	//set each light to correct values for given time
+		setTimeOfDay( TheGlobalData->m_timeOfDay );	//set each light to correct values for given time
 
-	for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++) 
-	{	m_3DScene->setGlobalLight( m_myLight[lindex], lindex );
-	}
+		for (lindex=0; lindex<TheGlobalData->m_numGlobalLights; lindex++) 
+		{	m_3DScene->setGlobalLight( m_myLight[lindex], lindex );
+		}
 
-#ifdef SAMPLE_DYNAMIC_LIGHT
-	theDynamicLight = NEW_REF(W3DDynamicLight, ());
-	Real red = 1;
-	Real green = 1;
-	Real blue = 0;
-	if(red==0 && blue==0 && green==0) {
-		red = green = blue = 1;
-	}
-	theDynamicLight->Set_Ambient( Vector3( red, green, blue ) );
-	theDynamicLight->Set_Diffuse( Vector3( red, green, blue) );
-	theDynamicLight->Set_Position(Vector3(0, 0, 4));
-	theDynamicLight->Set_Far_Attenuation_Range(1, 8);
-	// Note: Don't Add_Render_Object dynamic lights. 
-	m_3DScene->addDynamicLight( theDynamicLight );
-#endif
+	#ifdef SAMPLE_DYNAMIC_LIGHT
+		theDynamicLight = NEW_REF(W3DDynamicLight, ());
+		Real red = 1;
+		Real green = 1;
+		Real blue = 0;
+		if(red==0 && blue==0 && green==0) {
+			red = green = blue = 1;
+		}
+		theDynamicLight->Set_Ambient( Vector3( red, green, blue ) );
+		theDynamicLight->Set_Diffuse( Vector3( red, green, blue) );
+		theDynamicLight->Set_Position(Vector3(0, 0, 4));
+		theDynamicLight->Set_Far_Attenuation_Range(1, 8);
+		// Note: Don't Add_Render_Object dynamic lights. 
+		m_3DScene->addDynamicLight( theDynamicLight );
+	#endif
+
+	}
 
 	// create a new asset manager
 	m_assetManager = NEW W3DAssetManager;	
@@ -732,43 +740,35 @@ void W3DDisplay::init( void )
 	m_assetManager->Register_Prototype_Loader(&_AggregateLoader);
 	m_assetManager->Set_WW3D_Load_On_Demand( true );
 
-
-	if (TheGlobalData->m_incrementalAGPBuf)
+	if (!TheGlobalData->m_headless)
 	{
-		SortingRendererClass::SetMinVertexBufferSize(1);
-	}
-	if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK)
-		throw ERROR_INVALID_D3D;	//failed to initialize.  User probably doesn't have DX 8.1
 
-	WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS );
-	WW3D::Set_Collision_Box_Display_Mask(0x00);	///<set to 0xff to make collision boxes visible
-	WW3D::Enable_Static_Sort_Lists(true);
-	WW3D::Set_Thumbnail_Enabled(false);
-	WW3D::Set_Screen_UV_Bias( TRUE );  ///< this makes text look good :)
-	WW3D::Set_Texture_Bitdepth(32);
+		if (TheGlobalData->m_incrementalAGPBuf)
+		{
+			SortingRendererClass::SetMinVertexBufferSize(1);
+		}
+		if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK)
+			throw ERROR_INVALID_D3D;	//failed to initialize.  User probably doesn't have DX 8.1
+
+		WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS );
+		WW3D::Set_Collision_Box_Display_Mask(0x00);	///<set to 0xff to make collision boxes visible
+		WW3D::Enable_Static_Sort_Lists(true);
+		WW3D::Set_Thumbnail_Enabled(false);
+		WW3D::Set_Screen_UV_Bias( TRUE );  ///< this makes text look good :)
+		WW3D::Set_Texture_Bitdepth(32);
 			
-	setWindowed( TheGlobalData->m_windowed );
-
-	// create a 2D renderer helper
-	m_2DRender = NEW Render2DClass;
-	DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") );
-
-	// set our default width and height and bit depth
-	/// @todo we should set this according to options read from a file
-	setWidth( TheGlobalData->m_xResolution );
-	setHeight( TheGlobalData->m_yResolution );
-	setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH );
-
-	if( WW3D::Set_Render_Device( 0, 
-															 getWidth(), 
-															 getHeight(), 
-															 getBitDepth(), 
-															 getWindowed(), 
-															 true ) != WW3D_ERROR_OK ) 
-	{
-		// Getting the device at the default bit depth (32) didn't work, so try
-		// getting a 16 bit display.  (Voodoo 1-3 only supported 16 bit.) jba.
-		setBitDepth( 16 );
+		setWindowed( TheGlobalData->m_windowed );
+
+		// create a 2D renderer helper
+		m_2DRender = NEW Render2DClass;
+		DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") );
+
+		// set our default width and height and bit depth
+		/// @todo we should set this according to options read from a file
+		setWidth( TheGlobalData->m_xResolution );
+		setHeight( TheGlobalData->m_yResolution );
+		setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH );
+
 		if( WW3D::Set_Render_Device( 0, 
 																 getWidth(), 
 																 getHeight(), 
@@ -776,63 +776,78 @@ void W3DDisplay::init( void )
 																 getWindowed(), 
 																 true ) != WW3D_ERROR_OK ) 
 		{
+			// Getting the device at the default bit depth (32) didn't work, so try
+			// getting a 16 bit display.  (Voodoo 1-3 only supported 16 bit.) jba.
+			setBitDepth( 16 );
+			if( WW3D::Set_Render_Device( 0, 
+																	 getWidth(), 
+																	 getHeight(), 
+																	 getBitDepth(), 
+																	 getWindowed(), 
+																	 true ) != WW3D_ERROR_OK ) 
+			{
 
-			WW3D::Shutdown();
-			WWMath::Shutdown();
-			throw ERROR_INVALID_D3D;	//failed to initialize.  User probably doesn't have DX 8.1
-			DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") );
-			return;
-		}
+				WW3D::Shutdown();
+				WWMath::Shutdown();
+				throw ERROR_INVALID_D3D;	//failed to initialize.  User probably doesn't have DX 8.1
+				DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") );
+				return;
+			}
 
-	}  // end if
+		}  // end if
 
-	//Check if level was never set and default to setting most suitable for system.
-	if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN)
-		TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel());
-	else
-	{	//Static LOD level was applied during GameLOD manager init except for texture reduction
-		//which needs to be applied here.
-		Int txtReduction=TheWritableGlobalData->m_textureReductionFactor;
-		if (txtReduction > 0)
-		{		WW3D::Set_Texture_Reduction(txtReduction,32);
-				//Tell LOD manager that texture reduction was applied.
-				TheGameLODManager->setCurrentTextureReduction(txtReduction);
+		//Check if level was never set and default to setting most suitable for system.
+		if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN)
+			TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel());
+		else
+		{	//Static LOD level was applied during GameLOD manager init except for texture reduction
+			//which needs to be applied here.
+			Int txtReduction=TheWritableGlobalData->m_textureReductionFactor;
+			if (txtReduction > 0)
+			{		WW3D::Set_Texture_Reduction(txtReduction,32);
+					//Tell LOD manager that texture reduction was applied.
+					TheGameLODManager->setCurrentTextureReduction(txtReduction);
+			}
 		}
-	}
 
-	if (TheGlobalData->m_displayGamma != 1.0f)
-		setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE);
+		if (TheGlobalData->m_displayGamma != 1.0f)
+			setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE);
+	}
 
 	initAssets();
-	init2DScene();
-	init3DScene();
-	W3DShaderManager::init();
 
-	// Create and initialize the debug display
-	m_nativeDebugDisplay = NEW W3DDebugDisplay();
-	m_debugDisplay = m_nativeDebugDisplay;
-	if ( m_nativeDebugDisplay )
+	if (!TheGlobalData->m_headless)
 	{
-		m_nativeDebugDisplay->init();
-		GameFont *font;
+		init2DScene();
+		init3DScene();
+		W3DShaderManager::init();
 
-		if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty())
+		// Create and initialize the debug display
+		m_nativeDebugDisplay = NEW W3DDebugDisplay();
+		m_debugDisplay = m_nativeDebugDisplay;
+		if ( m_nativeDebugDisplay )
 		{
-			font=TheFontLibrary->getFont(
-				TheGlobalLanguageData->m_nativeDebugDisplay.name,
-				TheGlobalLanguageData->m_nativeDebugDisplay.size,
-				TheGlobalLanguageData->m_nativeDebugDisplay.bold);
+			m_nativeDebugDisplay->init();
+			GameFont *font;
+
+			if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty())
+			{
+				font=TheFontLibrary->getFont(
+					TheGlobalLanguageData->m_nativeDebugDisplay.name,
+					TheGlobalLanguageData->m_nativeDebugDisplay.size,
+					TheGlobalLanguageData->m_nativeDebugDisplay.bold);
+			}
+			else
+				font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );
+
+			m_nativeDebugDisplay->setFont( font );
+			m_nativeDebugDisplay->setFontHeight( 13 );
+			m_nativeDebugDisplay->setFontWidth( 9 );
 		}
-		else
-			font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );
 
-		m_nativeDebugDisplay->setFont( font );
-		m_nativeDebugDisplay->setFontHeight( 13 );
-		m_nativeDebugDisplay->setFontWidth( 9 );
+		DX8WebBrowser::Initialize();
 	}
 
-	DX8WebBrowser::Initialize();
-
 	// we're now online
 	m_initialized = true;
 	if( TheGlobalData->m_displayDebug )
@@ -852,16 +867,19 @@ void W3DDisplay::reset( void )
 
 	// Remove all render objects.
 
-	SceneIterator *sceneIter = m_3DScene->Create_Iterator();
-	sceneIter->First();
-	while(!sceneIter->Is_Done()) {
-		RenderObjClass * robj = sceneIter->Current_Item();
-		robj->Add_Ref();
-		m_3DScene->Remove_Render_Object(robj);
-		robj->Release_Ref();
-		sceneIter->Next();
+	if (m_3DScene != NULL)
+	{
+		SceneIterator *sceneIter = m_3DScene->Create_Iterator();
+		sceneIter->First();
+		while(!sceneIter->Is_Done()) {
+			RenderObjClass * robj = sceneIter->Current_Item();
+			robj->Add_Ref();
+			m_3DScene->Remove_Render_Object(robj);
+			robj->Release_Ref();
+			sceneIter->Next();
+		}
+		m_3DScene->Destroy_Iterator(sceneIter);
 	}
-	m_3DScene->Destroy_Iterator(sceneIter);
 
 	m_isClippedEnabled = FALSE;
 
@@ -1678,6 +1696,8 @@ void W3DDisplay::draw( void )
 		return;
 	}
 
+	if (TheGlobalData->m_headless)
+		return;
 
 	updateAverageFPS();
 	if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD())
@@ -2089,6 +2109,8 @@ void W3DDisplay::createLightPulse( const Coord3D *pos, const RGBColor *color,
 																	 UnsignedInt decayFrameTime//, Bool donut
 																	 )
 {
+	if (m_3DScene == NULL)
+		return;
 	if (innerRadius+attenuationWidth<2.0*PATHFIND_CELL_SIZE_F + 1.0f) {
 		return; // it basically won't make any visual difference.  jba.
 	}
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp
index 0204f68a..71bd8a9a 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp
@@ -232,22 +232,24 @@ void W3DTerrainVisual::init( void )
 	m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN );
 	TheTerrainRenderObject = m_terrainRenderObject;
 
-	// initialize track drawing system
-	TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem;
-	TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene);
+	if (!TheGlobalData->m_headless)
+	{
+		// initialize track drawing system
+		TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem;
+		TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene);
 
-	// initialize object shadow drawing system
-	TheW3DShadowManager = NEW W3DShadowManager;
- 	TheW3DShadowManager->init();
+		// initialize object shadow drawing system
+		TheW3DShadowManager = NEW W3DShadowManager;
+ 		TheW3DShadowManager->init();
 	
-	// create a water plane render object
-	TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () );
-	m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType);	//create a water plane that's 128x128 units
-	m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ));	//place water in world
+		// create a water plane render object
+		TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () );
+		m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType);	//create a water plane that's 128x128 units
+		m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ));	//place water in world
 
-	// create smudge rendering system.
-	TheSmudgeManager = NEW(W3DSmudgeManager);
-	TheSmudgeManager->init();
+		// create smudge rendering system.
+		TheSmudgeManager = NEW(W3DSmudgeManager);
+		TheSmudgeManager->init();
 
 #ifdef DO_UNIT_TIMINGS
 #pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")
@@ -261,10 +263,11 @@ void W3DTerrainVisual::init( void )
 			W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject);
 		}
 #endif
-	if (TheGlobalData->m_useCloudPlane)
-		m_waterRenderObject->toggleCloudLayer(true);
-	else
-		m_waterRenderObject->toggleCloudLayer(false);
+		if (TheGlobalData->m_useCloudPlane)
+			m_waterRenderObject->toggleCloudLayer(true);
+		else
+			m_waterRenderObject->toggleCloudLayer(false);
+	}
 
 	// set the vertex animated water properties
 	Int waterSettingIndex = 0;  // use index 0 settings by default
@@ -634,7 +637,7 @@ Bool W3DTerrainVisual::load( AsciiString filename )
 	}
 
 
-	RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
+	RefRenderObjListIterator *it = W3DDisplay::m_3DScene ? W3DDisplay::m_3DScene->createLightsIterator() : NULL;
 	// apply the heightmap to the terrain render object
 
 #ifdef DO_SEISMIC_SIMULATIONS
@@ -655,13 +658,17 @@ Bool W3DTerrainVisual::load( AsciiString filename )
 	 it = NULL;
 	}
 	// add our terrain render object to the scene
-	W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject );
+	if (W3DDisplay::m_3DScene != NULL)
+		W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject );
 
 #if defined RTS_DEBUG || defined RTS_INTERNAL
 	// Icon drawing utility object for pathfinding.
-	W3DDebugIcons *icons = NEW W3DDebugIcons;
- 	W3DDisplay::m_3DScene->Add_Render_Object( icons );
-	icons->Release_Ref(); // belongs to scene.
+	if (W3DDisplay::m_3DScene != NULL)
+	{
+		W3DDebugIcons *icons = NEW W3DDebugIcons;
+ 		W3DDisplay::m_3DScene->Add_Render_Object( icons );
+		icons->Release_Ref(); // belongs to scene.
+	}
 #endif
 
 #ifdef DO_UNIT_TIMINGS
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
index ac7bbe97..5ed7c1f8 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
@@ -560,6 +560,8 @@ void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *ray
 //-------------------------------------------------------------------------------------------------
 void W3DView::setCameraTransform( void )
 {
+	if (TheGlobalData->m_headless)
+		return;
 	m_cameraHasMovedSinceRequest = true;
 	Matrix3D cameraTransform( 1 );
 	
@@ -616,7 +618,7 @@ void W3DView::setCameraTransform( void )
 	buildCameraTransform( &cameraTransform );
 	m_3DCamera->Set_Transform( cameraTransform );
 
-	if (TheTerrainRenderObject) 
+	if (TheTerrainRenderObject)
 	{
 		RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
 		TheTerrainRenderObject->updateCenter(m_3DCamera, it);
@@ -1817,7 +1819,7 @@ void W3DView::scrollBy( Coord2D *delta )
 													  
 		start.X = getWidth();
 		start.Y = getHeight();
-		Real aspect = getWidth()/getHeight();
+		Real aspect = getHeight() == 0 ? 1 : getWidth()/getHeight();
 		end.X = start.X + delta->x * SCROLL_RESOLUTION;
 		end.Y = start.Y + delta->y * SCROLL_RESOLUTION*aspect;
 
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp
index 8dd1de15..a3e4ed6a 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp
@@ -35,6 +35,7 @@
 #include "Common/PlayerList.h"
 #include "Common/ThingTemplate.h"
 #include "Common/Xfer.h"
+#include "Common/GlobalData.h"
 #include "GameClient/Drawable.h"
 #include "GameClient/GameClient.h"
 #include "GameLogic/GameLogic.h"
@@ -155,7 +156,8 @@ void W3DRenderObjectSnapshot::update(RenderObjClass *robj, DrawableInfo *drawInf
 // ------------------------------------------------------------------------------------------------
 void W3DRenderObjectSnapshot::addToScene(void)
 {
-	((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj);
+	if (W3DDisplay::m_3DScene != NULL)
+		((SimpleSceneClass *)W3DDisplay::m_3DScene)->Add_Render_Object(m_robj);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -433,6 +435,8 @@ void W3DGhostObject::removeParentObject(void)
 // ------------------------------------------------------------------------------------------------
 void W3DGhostObject::restoreParentObject(void)
 {
+	if (TheGlobalData->m_headless)
+		return;
 	Drawable *draw=m_parentObject->getDrawable();
 	if (!draw)
 		return;
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp
index f174eb24..181fb121 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp
@@ -281,6 +281,8 @@ Real W3DTerrainLogic::getGroundHeight( Real x, Real y, Coord3D* normal ) const
 {
 #define USE_THE_TERRAIN_OBJECT
 #ifdef USE_THE_TERRAIN_OBJECT
+	// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+	// W3DTerrainLogic shouldn't depend on TheTerrainRenderObject!
 	if (TheTerrainRenderObject) 
 	{
 		return TheTerrainRenderObject->getHeightMapHeight(x,y,normal);
@@ -358,7 +360,8 @@ Real W3DTerrainLogic::getLayerHeight( Real x, Real y, PathfindLayerEnum layer, C
 //-------------------------------------------------------------------------------------------------
 Bool W3DTerrainLogic::isCliffCell( Real x, Real y) const
 {
-
+	// TheSuperHackers @logic-client-separation helmutbuhler 11/04/2025
+	// W3DTerrainLogic shouldn't depend on TheTerrainRenderObject!
 	return TheTerrainRenderObject->isCliffCell(x,y);
 
 }  // end isCliffCell
diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp
index b365a7ec..c7877ecb 100644
--- a/GeneralsMD/Code/Main/WinMain.cpp
+++ b/GeneralsMD/Code/Main/WinMain.cpp
@@ -746,8 +746,7 @@ static Bool initializeAppWindows( HINSTANCE hInstance, Int nCmdShow, Bool runWin
 	ShowWindow( hWnd, nCmdShow );
 	UpdateWindow( hWnd );
 
-	// save our application instance and window handle for future use
-	ApplicationHInstance = hInstance;
+	// save our application window handle for future use
 	ApplicationHWnd = hWnd;
 	gInitializing = false;
 	if (!runWindowed) {
@@ -886,13 +885,21 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
 		char * argv[20];
 		argv[0] = NULL;
 
+		Bool headless = false;
+
 		char *token;
 		token = nextParam(lpCmdLine, "\" ");
 		while (argc < 20 && token != NULL) {
 			argv[argc++] = strtrim(token);
+			
 			//added a preparse step for this flag because it affects window creation style
-			if (stricmp(token,"-win")==0)
-				ApplicationIsWindowed=true;
+			if (stricmp(token, "-win") == 0)
+				ApplicationIsWindowed = true;
+
+			// preparse for headless as well. We need to know about this before we create the window.
+			if (stricmp(token, "-headless") == 0)
+				headless = true;
+			
 			token = nextParam(NULL, "\" ");	   
 		}
 
@@ -951,10 +958,12 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
 		gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE);
 #endif
 
-
 		// register windows class and create application window
-		if( initializeAppWindows( hInstance, nCmdShow, ApplicationIsWindowed) == false )
+		if(!headless && initializeAppWindows(hInstance, nCmdShow, ApplicationIsWindowed) == false)
 			return 0;
+		
+		// save our application instance for future use
+		ApplicationHInstance = hInstance;
 
 		if (gLoadScreenBitmap!=NULL) {
 			::DeleteObject(gLoadScreenBitmap);

And here is the log

Details
git apply -p2 --directory=Generals --reject --whitespace=fix changes.patch
changes.patch:75: trailing whitespace.

changes.patch:114: trailing whitespace.
//
changes.patch:187: trailing whitespace.


                         WindowMsgData mData1,
changes.patch:417: trailing whitespace.

changes.patch:440: space before tab in indent.
        TheMouse->setName("TheMouse");
Checking patch Generals/Code/GameEngine/Include/Common/GameAudio.h...
Hunk #1 succeeded at 371 (offset -1 lines).
Checking patch Generals/Code/GameEngine/Include/Common/GlobalData.h...
Hunk #1 succeeded at 96 (offset -4 lines).
Checking patch Generals/Code/GameEngine/Include/Common/Radar.h...
Hunk #1 succeeded at 291 (offset -2 lines).
Checking patch Generals/Code/GameEngine/Include/GameClient/GameClient.h...
Hunk #1 succeeded at 227 (offset -32 lines).
Checking patch Generals/Code/GameEngine/Include/GameClient/GameWindow.h...
Hunk #1 succeeded at 320 (offset -2 lines).
Hunk #2 succeeded at 424 (offset -2 lines).
Checking patch Generals/Code/GameEngine/Include/GameClient/GameWindowManager.h...
Checking patch Generals/Code/GameEngine/Include/GameClient/Mouse.h...
Hunk #1 succeeded at 370 (offset -1 lines).
Checking patch Generals/Code/GameEngine/Source/Common/CommandLine.cpp...
Checking patch Generals/Code/GameEngine/Source/Common/GameEngine.cpp...
error: while searching for:
  startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
        DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
        #endif/////////////////////////////////////////////////////////////////////////////////////////////
                initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL);
                if (!TheAudio->isMusicAlreadyLoaded())
                        setQuitting(TRUE);


error: patch failed: Generals/Code/GameEngine/Source/Common/GameEngine.cpp:431
error: while searching for:
                initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini");
                initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL);
                initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL);
                initSubsystem(TheRadar,"TheRadar", createRadar(), NULL);
                initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL);



error: patch failed: Generals/Code/GameEngine/Source/Common/GameEngine.cpp:507
Checking patch Generals/Code/GameEngine/Source/Common/GlobalData.cpp...
Hunk #1 succeeded at 608 (offset -5 lines).
Checking patch Generals/Code/GameEngine/Source/Common/RandomValue.cpp...
Checking patch Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp...
error: while searching for:
        { "Overridable", 32, 32 },

        { "W3DGameWindow", 700, 256 },
        { "SuccessState", 32, 32 },
        { "FailureState", 32, 32 },
        { "ContinueState", 32, 32 },

error: patch failed: Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp:416
Checking patch Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp...
Hunk #1 succeeded at 1359 (offset -23 lines).
Checking patch Generals/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp...
Checking patch Generals/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp...
Checking patch Generals/Code/GameEngine/Source/GameClient/GameClient.cpp...
Hunk #1 succeeded at 262 (offset -8 lines).
Hunk #2 succeeded at 317 (offset -8 lines).
Hunk #3 succeeded at 335 (offset -8 lines).
error: while searching for:
        //Initial Game Codition.  We must show the movie first and then we can display the shell
        if(TheGlobalData->m_afterIntro && !TheDisplay->isMoviePlaying())
        {
                if( playSizzle && TheGlobalData->m_playSizzle )
                {
                        TheWritableGlobalData->m_allowExitOutOfMovies = TRUE;
                        if(TheGameLODManager && TheGameLODManager->didMemPass())

error: patch failed: Generals/Code/GameEngine/Source/GameClient/GameClient.cpp:535
Hunk #5 succeeded at 591 (offset -38 lines).
Hunk #6 succeeded at 713 (offset -38 lines).
Checking patch Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp...
Hunk #1 succeeded at 2213 (offset -5 lines).
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp...
Hunk #1 succeeded at 5243 (offset -851 lines).
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp...
error: while searching for:
                        if( m_numberApproachPositions != DYNAMIC_APPROACH_VECTOR_FLAG )
                        {
                                // Dynamic means no bones
                                // TheSuperHackers @fix helmutbuhler 19/04/2025 Zero initialize array to prevent uninitialized memory reads.
                                // Important: the entire target vector is used for serialization and crc and must not contain random data.
                                Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE] = {0};

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp:510
Checking patch Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp...
Hunk #1 succeeded at 2386 (offset -212 lines).
Checking patch Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp...
error: while searching for:
                // Prevent from selecting the disabled Generals.
                // This is also enforced at GUI setup (GUIUtil.cpp).
                // @todo: unlock these when something rad happens
                Bool disallowLockedGenerals = TRUE;
                const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName());
                Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp:736
error: while searching for:
        //****************************//

        // Get the m_loadScreen for this kind of game
        if(!m_loadScreen)
        {
                m_loadScreen = getLoadScreen( loadingSaveGame );
                if(m_loadScreen)

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp:1266
error: while searching for:
                                // The game will start, but the cheater will be instantly defeated because he has no troops.
                                // This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp).
                                // @todo: unlock these when something rad happens
                                Bool disallowLockedGenerals = TRUE;
                                const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName());
                                Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp:1981
error: while searching for:
        }

        // if we're in a load game, don't fade yet
        if( loadingSaveGame == FALSE )
        {
                TheTransitionHandler->setGroup("FadeWholeScreen");
                while(!TheTransitionHandler->isFinished())

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp:2186
Hunk #5 succeeded at 1927 (offset -305 lines).
Checking patch Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp...
error: while searching for:
        TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows.

        Bool shellGame = FALSE;
        if ((!isInShellGame() || !isInGame()) && showScoreScreen)
        {
                shellGame = TRUE;
                TheTransitionHandler->setGroup("FadeWholeScreen");

error: patch failed: Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp:265
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp...
error: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp: No such file or directory
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp...
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp...
Hunk #1 succeeded at 202 (offset -3 lines).
Hunk #2 succeeded at 227 (offset -3 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp...
Hunk #1 succeeded at 2749 (offset -59 lines).
Hunk #2 succeeded at 3077 (offset -59 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp...
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp...
error: while searching for:
        DEBUG_ASSERTCRASH(y0<=y1, ("HeightMapRenderObjClass::UpdateBlock parameters have inside-out rectangle (on Y)."));
#endif
        Invalidate_Cached_Bounding_Volumes();
        if (pMap) {
                REF_PTR_SET(m_stageZeroTexture, pMap->getTerrainTexture());
                REF_PTR_SET(m_stageOneTexture, pMap->getAlphaTerrainTexture());
        }

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp:1026
error: while searching for:
        if (m_stageOneTexture == NULL) {
                needToAllocate = true;
        }
        if (data && needToAllocate)
        {       //requested heightmap different from old one.
                freeIndexVertexBuffers();
                //Create static index buffers.  These will index the vertex buffers holding the map.

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp:1326
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp...
Hunk #1 succeeded at 823 (offset -32 lines).
Hunk #2 succeeded at 842 (offset -32 lines).
Hunk #3 succeeded at 890 (offset -32 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp...
Hunk #1 succeeded at 765 (offset -3 lines).
Hunk #2 succeeded at 807 (offset -3 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp...
error: while searching for:

        // shutdown
        Debug_Statistics::Shutdown_Statistics();
        W3DShaderManager::shutdown();
        m_assetManager->Free_Assets();
        delete m_assetManager;
        WW3D::Shutdown();
        WWMath::Shutdown();
        DX8WebBrowser::Shutdown();
        delete TheW3DFileSystem;
        TheW3DFileSystem = NULL;


error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp:464
Hunk #2 succeeded at 616 (offset -69 lines).
error: while searching for:
        m_assetManager->Register_Prototype_Loader(&_AggregateLoader);
        m_assetManager->Set_WW3D_Load_On_Demand( true );


        if (TheGlobalData->m_incrementalAGPBuf)
        {
                SortingRendererClass::SetMinVertexBufferSize(1);
        }
        if (WW3D::Init( ApplicationHWnd ) != WW3D_ERROR_OK)
                throw ERROR_INVALID_D3D;        //failed to initialize.  User probably doesn't have DX 8.1

        WW3D::Set_Prelit_Mode( WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS );
        WW3D::Set_Collision_Box_Display_Mask(0x00);     ///<set to 0xff to make collision boxes visible
        WW3D::Enable_Static_Sort_Lists(true);
        WW3D::Set_Thumbnail_Enabled(false);
        WW3D::Set_Screen_UV_Bias( TRUE );  ///< this makes text look good :)
        WW3D::Set_Texture_Bitdepth(32);

        setWindowed( TheGlobalData->m_windowed );

        // create a 2D renderer helper
        m_2DRender = NEW Render2DClass;
        DEBUG_ASSERTCRASH( m_2DRender, ("Cannot create Render2DClass") );

        // set our default width and height and bit depth
        /// @todo we should set this according to options read from a file
        setWidth( TheGlobalData->m_xResolution );
        setHeight( TheGlobalData->m_yResolution );
        setBitDepth( W3D_DISPLAY_DEFAULT_BIT_DEPTH );

        if( WW3D::Set_Render_Device( 0,

         getWidth(),

         getHeight(),

         getBitDepth(),

         getWindowed(),

         true ) != WW3D_ERROR_OK )
        {
                // Getting the device at the default bit depth (32) didn't work, so try
                // getting a 16 bit display.  (Voodoo 1-3 only supported 16 bit.) jba.
                setBitDepth( 16 );
                if( WW3D::Set_Render_Device( 0,

                 getWidth(),

                 getHeight(),

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp:732
error: while searching for:

                 getWindowed(),

                 true ) != WW3D_ERROR_OK )
                {

                        WW3D::Shutdown();
                        WWMath::Shutdown();
                        throw ERROR_INVALID_D3D;        //failed to initialize.  User probably doesn't have DX 8.1
                        DEBUG_ASSERTCRASH( 0, ("Unable to set render device\n") );
                        return;
                }

        }  // end if

        //Check if level was never set and default to setting most suitable for system.
        if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN)
                TheGameLODManager->setStaticLODLevel(TheGameLODManager->findStaticLODLevel());
        else
        {       //Static LOD level was applied during GameLOD manager init except for texture reduction
                //which needs to be applied here.
                Int txtReduction=TheWritableGlobalData->m_textureReductionFactor;
                if (txtReduction > 0)
                {               WW3D::Set_Texture_Reduction(txtReduction,32);
                                //Tell LOD manager that texture reduction was applied.
                                TheGameLODManager->setCurrentTextureReduction(txtReduction);
                }
        }

        if (TheGlobalData->m_displayGamma != 1.0f)
                setGamma(TheGlobalData->m_displayGamma,0.0f,1.0f,FALSE);

        initAssets();
        init2DScene();
        init3DScene();
        W3DShaderManager::init();

        // Create and initialize the debug display
        m_nativeDebugDisplay = NEW W3DDebugDisplay();
        m_debugDisplay = m_nativeDebugDisplay;
        if ( m_nativeDebugDisplay )
        {
                m_nativeDebugDisplay->init();
                GameFont *font;

                if (TheGlobalLanguageData && TheGlobalLanguageData->m_nativeDebugDisplay.name.isNotEmpty())
                {
                        font=TheFontLibrary->getFont(
                                TheGlobalLanguageData->m_nativeDebugDisplay.name,
                                TheGlobalLanguageData->m_nativeDebugDisplay.size,
                                TheGlobalLanguageData->m_nativeDebugDisplay.bold);
                }
                else
                        font=TheFontLibrary->getFont( AsciiString("FixedSys"), 8, FALSE );

                m_nativeDebugDisplay->setFont( font );
                m_nativeDebugDisplay->setFontHeight( 13 );
                m_nativeDebugDisplay->setFontWidth( 9 );
        }

        DX8WebBrowser::Initialize();

        // we're now online
        m_initialized = true;
        if( TheGlobalData->m_displayDebug )

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp:776
Hunk #5 succeeded at 790 (offset -77 lines).
Hunk #6 succeeded at 1598 (offset -98 lines).
Hunk #7 succeeded at 1982 (offset -127 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp...
error: while searching for:
        m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN );
        TheTerrainRenderObject = m_terrainRenderObject;

        // initialize track drawing system
        TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem;
        TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene);

        // initialize object shadow drawing system
        TheW3DShadowManager = NEW W3DShadowManager;
        TheW3DShadowManager->init();

        // create a water plane render object
        TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () );
        m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType);    //create a water plane that's 128x128 units
        m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ));    //place water in world

        // create smudge rendering system.
        TheSmudgeManager = NEW(W3DSmudgeManager);
        TheSmudgeManager->init();

#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp:232
Hunk #2 succeeded at 134 (offset -129 lines).
error: while searching for:
        }


        RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
        // apply the heightmap to the terrain render object

#ifdef DO_SEISMIC_SIMULATIONS

error: patch failed: Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp:634
Hunk #4 succeeded at 305 (offset -353 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp...
Hunk #1 succeeded at 426 (offset -134 lines).
Hunk #2 succeeded at 474 (offset -144 lines).
Hunk #3 succeeded at 1656 (offset -163 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp...
Hunk #2 succeeded at 152 (offset -4 lines).
Hunk #3 succeeded at 431 (offset -4 lines).
Checking patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp...
Checking patch Generals/Code/Main/WinMain.cpp...
Hunk #1 succeeded at 724 (offset -22 lines).
Hunk #2 succeeded at 860 (offset -25 lines).
error: while searching for:
                gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE);
#endif


                // register windows class and create application window
                if( initializeAppWindows( hInstance, nCmdShow, ApplicationIsWindowed) == false )
                        return 0;

                if (gLoadScreenBitmap!=NULL) {
                        ::DeleteObject(gLoadScreenBitmap);

error: patch failed: Generals/Code/Main/WinMain.cpp:951
Applied patch Generals/Code/GameEngine/Include/Common/GameAudio.h cleanly.
Applied patch Generals/Code/GameEngine/Include/Common/GlobalData.h cleanly.
Applied patch Generals/Code/GameEngine/Include/Common/Radar.h cleanly.
Applied patch Generals/Code/GameEngine/Include/GameClient/GameClient.h cleanly.
Applied patch Generals/Code/GameEngine/Include/GameClient/GameWindow.h cleanly.
Applied patch Generals/Code/GameEngine/Include/GameClient/GameWindowManager.h cleanly.
Applied patch Generals/Code/GameEngine/Include/GameClient/Mouse.h cleanly.
Applied patch Generals/Code/GameEngine/Source/Common/CommandLine.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/Common/GameEngine.cpp with 2 rejects...
Rejected hunk #1.
Rejected hunk #2.
Applied patch Generals/Code/GameEngine/Source/Common/GlobalData.cpp cleanly.
Applied patch Generals/Code/GameEngine/Source/Common/RandomValue.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp with 1 reject...
Rejected hunk #1.
Applied patch Generals/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBar.cpp cleanly.
Applied patch Generals/Code/GameEngine/Source/GameClient/GUI/Gadget/GadgetListBox.cpp cleanly.
Applied patch Generals/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/GameClient/GameClient.cpp with 1 reject...
Hunk #1 applied cleanly.
Hunk #2 applied cleanly.
Hunk #3 applied cleanly.
Rejected hunk #4.
Hunk #5 applied cleanly.
Hunk #6 applied cleanly.
Applied patch Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp cleanly.
Applied patch Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp with 1 reject...
Rejected hunk #1.
Applied patch Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp cleanly.
Applying patch Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp with 4 rejects...
Rejected hunk #1.
Rejected hunk #2.
Rejected hunk #3.
Rejected hunk #4.
Hunk #5 applied cleanly.
Applying patch Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp with 1 reject...
Rejected hunk #1.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DDebrisDraw.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DLaserDraw.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DRopeDraw.cpp cleanly.
Applying patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/HeightMap.cpp with 2 rejects...
Rejected hunk #1.
Rejected hunk #2.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DBridgeBuffer.cpp cleanly.
Applying patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp with 3 rejects...
Rejected hunk #1.
Hunk #2 applied cleanly.
Rejected hunk #3.
Rejected hunk #4.
Hunk #5 applied cleanly.
Hunk #6 applied cleanly.
Hunk #7 applied cleanly.
Applying patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp with 2 rejects...
Rejected hunk #1.
Hunk #2 applied cleanly.
Rejected hunk #3.
Hunk #4 applied cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DGhostObject.cpp cleanly.
Applied patch Generals/Code/GameEngineDevice/Source/W3DDevice/GameLogic/W3DTerrainLogic.cpp cleanly.
Applying patch Generals/Code/Main/WinMain.cpp with 1 reject...
Hunk #1 applied cleanly.
Hunk #2 applied cleanly.
Rejected hunk #3.

@helmutbuhler helmutbuhler mentioned this pull request May 22, 2025
1 task
@xezon xezon added Enhancement Is new feature or request Major Severity: Minor < Major < Critical < Blocker Gen Relates to Generals Debug Is mostly debug functionality and removed Enhancement Is new feature or request labels May 22, 2025
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@helmutbuhler helmutbuhler merged commit c9d1fd5 into TheSuperHackers:main May 22, 2025
11 checks passed
@xezon xezon added the Enhancement Is new feature or request label May 23, 2025
fbraz3 pushed a commit to fbraz3/GeneralsX that referenced this pull request Nov 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Debug Is mostly debug functionality Enhancement Is new feature or request Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants