Skip to content

Commit 7dd0a99

Browse files
committed
refactor(headless): Add dummy implementations to reduce headless checks at call sites
1 parent ce11bb5 commit 7dd0a99

12 files changed

Lines changed: 130 additions & 42 deletions

File tree

Core/GameEngine/Include/GameClient/TerrainVisual.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,47 @@ class TerrainVisual : public Snapshot,
306306

307307
};
308308

309+
// TheSuperHackers @feature bobtista 31/01/2026
310+
// TerrainVisual that does nothing. Used for Headless Mode.
311+
class TerrainVisualDummy : public TerrainVisual
312+
{
313+
public:
314+
virtual void getTerrainColorAt( Real x, Real y, RGBColor *pColor ) {}
315+
virtual TerrainType *getTerrainTile( Real x, Real y ) { return nullptr; }
316+
virtual void enableWaterGrid( Bool enable ) {}
317+
virtual void setWaterGridHeightClamps( const WaterHandle *waterTable, Real minZ, Real maxZ ) {}
318+
virtual void setWaterAttenuationFactors( const WaterHandle *waterTable, Real a, Real b, Real c, Real range ) {}
319+
virtual void setWaterTransform( const WaterHandle *waterTable, Real angle, Real x, Real y, Real z ) {}
320+
virtual void setWaterTransform( const Matrix3D *transform ) {}
321+
virtual void getWaterTransform( const WaterHandle *waterTable, Matrix3D *transform ) {}
322+
virtual void setWaterGridResolution( const WaterHandle *waterTable, Real gridCellsX, Real gridCellsY, Real cellSize ) {}
323+
virtual void getWaterGridResolution( const WaterHandle *waterTable, Real *gridCellsX, Real *gridCellsY, Real *cellSize ) {}
324+
virtual void changeWaterHeight( Real x, Real y, Real delta ) {}
325+
virtual void addWaterVelocity( Real worldX, Real worldY, Real velocity, Real preferredHeight ) {}
326+
virtual Bool getWaterGridHeight( Real worldX, Real worldY, Real *height) { return FALSE; }
327+
virtual void setTerrainTracksDetail(void) {}
328+
virtual void setShoreLineDetail(void) {}
329+
virtual void addFactionBib(Object *factionBuilding, Bool highlight, Real extra = 0) {}
330+
virtual void removeFactionBib(Object *factionBuilding) {}
331+
virtual void addFactionBibDrawable(Drawable *factionBuilding, Bool highlight, Real extra = 0) {}
332+
virtual void removeFactionBibDrawable(Drawable *factionBuilding) {}
333+
virtual void removeAllBibs(void) {}
334+
virtual void removeBibHighlighting(void) {}
335+
virtual void removeTreesAndPropsForConstruction( const Coord3D* pos, const GeometryInfo& geom, Real angle ) {}
336+
virtual void addProp(const ThingTemplate *tt, const Coord3D *pos, Real angle) {}
337+
virtual void setRawMapHeight(const ICoord2D *gridPos, Int height) {}
338+
virtual Int getRawMapHeight(const ICoord2D *gridPos) { return 0; }
339+
#ifdef DO_SEISMIC_SIMULATIONS
340+
virtual void updateSeismicSimulations( void ) {}
341+
virtual void addSeismicSimulation( const SeismicSimulationNode& sim ) {}
342+
#endif
343+
virtual void replaceSkyboxTextures(const AsciiString *oldTexName[NumSkyboxTextures], const AsciiString *newTexName[NumSkyboxTextures]) {}
344+
345+
protected:
346+
virtual void crc( Xfer *xfer ) {}
347+
virtual void xfer( Xfer *xfer ) {}
348+
virtual void loadPostProcess( void ) {}
349+
};
350+
309351
// EXTERNALS //////////////////////////////////////////////////////////////////////////////////////
310352
extern TerrainVisual *TheTerrainVisual; ///< singleton extern

Core/GameEngine/Include/GameClient/View.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,5 +333,27 @@ class ViewLocation
333333
}
334334
};
335335

336+
// TheSuperHackers @feature bobtista 31/01/2026
337+
// View that does nothing. Used for Headless Mode.
338+
class ViewDummy : public View
339+
{
340+
public:
341+
virtual Drawable *pickDrawable( const ICoord2D *screen, Bool forceAttack, PickType pickType ) { return nullptr; }
342+
virtual Int iterateDrawablesInRegion( IRegion2D *screenRegion, Bool (*callback)( Drawable *draw, void *userData ), void *userData ) { return 0; }
343+
virtual void forceRedraw() {}
344+
virtual const Coord3D& get3DCameraPosition() const { static Coord3D zero = {0,0,0}; return zero; }
345+
virtual WorldToScreenReturn worldToScreenTriReturn(const Coord3D *w, ICoord2D *s ) { return WTS_INVALID; }
346+
virtual void screenToWorld( const ICoord2D *s, Coord3D *w ) {}
347+
virtual void screenToTerrain( const ICoord2D *screen, Coord3D *world ) {}
348+
virtual void screenToWorldAtZ( const ICoord2D *s, Coord3D *w, Real z ) {}
349+
virtual void drawView( void ) {}
350+
virtual void updateView(void) {}
351+
virtual void stepView() {}
352+
virtual void setGuardBandBias( const Coord2D *gb ) {}
353+
354+
protected:
355+
virtual void xfer( Xfer *xfer ) {}
356+
};
357+
336358
// EXTERNALS //////////////////////////////////////////////////////////////////////////////////////
337359
extern View *TheTacticalView; ///< the main tactical interface to the game world

GeneralsMD/Code/GameEngine/Include/GameClient/GameWindowManager.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,16 @@ class GameWindowManagerDummy : public GameWindowManager
389389

390390
virtual GameWindow *allocateNewWindow() { return newInstance(GameWindowDummy); }
391391

392+
// TheSuperHackers @fix bobtista 31/01/2026 Return nullptr for message boxes in headless mode
393+
virtual GameWindow *gogoMessageBox(Int x, Int y, Int width, Int height, UnsignedShort buttonFlags,
394+
UnicodeString titleString, UnicodeString bodyString,
395+
GameWinMsgBoxFunc yesCallback, GameWinMsgBoxFunc noCallback,
396+
GameWinMsgBoxFunc okCallback, GameWinMsgBoxFunc cancelCallback) { return nullptr; }
397+
virtual GameWindow *gogoMessageBox(Int x, Int y, Int width, Int height, UnsignedShort buttonFlags,
398+
UnicodeString titleString, UnicodeString bodyString,
399+
GameWinMsgBoxFunc yesCallback, GameWinMsgBoxFunc noCallback,
400+
GameWinMsgBoxFunc okCallback, GameWinMsgBoxFunc cancelCallback, Bool useLogo) { return nullptr; }
401+
392402
virtual GameWinDrawFunc getPushButtonImageDrawFunc() { return nullptr; }
393403
virtual GameWinDrawFunc getPushButtonDrawFunc() { return nullptr; }
394404
virtual GameWinDrawFunc getCheckBoxImageDrawFunc() { return nullptr; }

GeneralsMD/Code/GameEngine/Include/GameClient/ParticleSys.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,21 @@ class ParticleSystemManager : public SubsystemInterface,
813813
TemplateMap m_templateMap; ///< a hash map of all particle system templates
814814
};
815815

816+
// TheSuperHackers @feature bobtista 31/01/2026
817+
// ParticleSystemManager that does nothing. Used for Headless Mode.
818+
class ParticleSystemManagerDummy : public ParticleSystemManager
819+
{
820+
public:
821+
virtual Int getOnScreenParticleCount( void ) { return 0; }
822+
virtual void doParticles(RenderInfoClass &rinfo) {}
823+
virtual void queueParticleRender() {}
824+
825+
protected:
826+
virtual void crc( Xfer *xfer ) {}
827+
virtual void xfer( Xfer *xfer ) {}
828+
virtual void loadPostProcess( void ) {}
829+
};
830+
816831
/// The particle system manager singleton
817832
extern ParticleSystemManager *TheParticleSystemManager;
818833

GeneralsMD/Code/GameEngine/Include/GameLogic/GhostObject.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,19 @@ inline Bool GhostObjectManager::trackAllPlayers() const
108108
#endif
109109
}
110110

111+
// TheSuperHackers @feature bobtista 19/01/2026
112+
// GhostObjectManager that does nothing for headless mode.
113+
// Note: Does NOT override crc/xfer/loadPostProcess to maintain save compatibility.
114+
class GhostObjectManagerDummy : public GhostObjectManager
115+
{
116+
public:
117+
virtual void reset(void) {}
118+
virtual GhostObject *addGhostObject(Object *object, PartitionData *pd) { return nullptr; }
119+
virtual void removeGhostObject(GhostObject *mod) {}
120+
virtual void updateOrphanedObjects(int *playerIndexList, int playerIndexCount) {}
121+
virtual void releasePartitionData(void) {}
122+
virtual void restorePartitionData(void) {}
123+
};
124+
111125
// the singleton
112126
extern GhostObjectManager *TheGhostObjectManager;

GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ void GameEngine::init()
541541
initSubsystem(TheCaveSystem,"TheCaveSystem", MSGNEW("GameEngineSubsystem") CaveSystem(), nullptr);
542542
initSubsystem(TheRankInfoStore,"TheRankInfoStore", MSGNEW("GameEngineSubsystem") RankInfoStore(), &xferCRC, nullptr, "Data\\INI\\Rank");
543543
initSubsystem(ThePlayerTemplateStore,"ThePlayerTemplateStore", MSGNEW("GameEngineSubsystem") PlayerTemplateStore(), &xferCRC, "Data\\INI\\Default\\PlayerTemplate", "Data\\INI\\PlayerTemplate");
544-
initSubsystem(TheParticleSystemManager,"TheParticleSystemManager", createParticleSystemManager(), nullptr);
544+
initSubsystem(TheParticleSystemManager,"TheParticleSystemManager", TheGlobalData->m_headless ? NEW ParticleSystemManagerDummy : createParticleSystemManager(), nullptr);
545545

546546
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
547547
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////

GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,7 @@ SaveCode GameState::saveGame( AsciiString filename, UnicodeString desc,
564564
xferSave.open( filepath );
565565
} catch(...) {
566566
// print error message to the user
567-
if (TheGlobalData && !TheGlobalData->m_headless)
568-
TheInGameUI->message( "GUI:Error" );
567+
TheInGameUI->message( "GUI:Error" );
569568
DEBUG_LOG(( "Error opening file '%s'", filepath.str() ));
570569
return SC_ERROR;
571570
}
@@ -595,16 +594,13 @@ SaveCode GameState::saveGame( AsciiString filename, UnicodeString desc,
595594
catch( ... )
596595
{
597596

598-
if (TheGlobalData && !TheGlobalData->m_headless)
599-
{
600-
UnicodeString ufilepath;
601-
ufilepath.translate(filepath);
597+
UnicodeString ufilepath;
598+
ufilepath.translate(filepath);
602599

603-
UnicodeString msg;
604-
msg.format( TheGameText->fetch("GUI:ErrorSavingGame"), ufilepath.str() );
600+
UnicodeString msg;
601+
msg.format( TheGameText->fetch("GUI:ErrorSavingGame"), ufilepath.str() );
605602

606-
MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, nullptr);
607-
}
603+
MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, nullptr);
608604

609605
// close the file and get out of here
610606
xferSave.close();
@@ -616,11 +612,8 @@ SaveCode GameState::saveGame( AsciiString filename, UnicodeString desc,
616612
xferSave.close();
617613

618614
// print message to the user for game successfully saved
619-
if (TheGlobalData && !TheGlobalData->m_headless)
620-
{
621-
UnicodeString msg = TheGameText->fetch( "GUI:GameSaveComplete" );
622-
TheInGameUI->message( msg );
623-
}
615+
UnicodeString msg = TheGameText->fetch( "GUI:GameSaveComplete" );
616+
TheInGameUI->message( msg );
624617

625618
return SC_OK;
626619

@@ -727,16 +720,13 @@ SaveCode GameState::loadGame( AvailableGameInfo gameInfo )
727720
TheGameEngine->reset();
728721

729722
// print error message to the user
730-
if (TheGlobalData && !TheGlobalData->m_headless)
731-
{
732-
UnicodeString ufilepath;
733-
ufilepath.translate(filepath);
723+
UnicodeString ufilepath;
724+
ufilepath.translate(filepath);
734725

735-
UnicodeString msg;
736-
msg.format( TheGameText->fetch("GUI:ErrorLoadingGame"), ufilepath.str() );
726+
UnicodeString msg;
727+
msg.format( TheGameText->fetch("GUI:ErrorLoadingGame"), ufilepath.str() );
737728

738-
MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, nullptr);
739-
}
729+
MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, nullptr);
740730

741731
return SC_INVALID_DATA; // you can't use a naked "throw" outside of a catch statement!
742732

@@ -1383,17 +1373,6 @@ void GameState::xferSaveData( Xfer *xfer, SnapshotType which )
13831373
continue;
13841374
}
13851375

1386-
// Skip visual-only blocks when saving in headless mode
1387-
if( TheGlobalData && TheGlobalData->m_headless &&
1388-
(blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 ||
1389-
blockName.compareNoCase( "CHUNK_TacticalView" ) == 0 ||
1390-
blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 ||
1391-
blockName.compareNoCase( "CHUNK_GhostObject" ) == 0) )
1392-
{
1393-
DEBUG_LOG(("Skipping block '%s' in headless mode", blockName.str()));
1394-
continue;
1395-
}
1396-
13971376
//
13981377
// for mission save files, we only save the game state block and campaign manager
13991378
// because anything else is not needed.

GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ void GameClient::init( void )
381381
TheTerrainVisual = createTerrainVisual();
382382
if( TheTerrainVisual ) {
383383
TheTerrainVisual->init();
384-
TheTerrainVisual->setName("TheTerrainVisual");
384+
TheTerrainVisual->setName("TheTerrainVisual");
385385
}
386386

387387
// allocate the ray effects manager

GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,17 +1270,17 @@ void InGameUI::init( void )
12701270
been moved to where all the other translators are attached in game client */
12711271

12721272
// create the tactical view
1273-
if (TheDisplay)
1273+
TheTacticalView = createView();
1274+
if (TheTacticalView && TheDisplay)
12741275
{
1275-
TheTacticalView = createView();
12761276
TheTacticalView->init();
12771277
TheDisplay->attachView( TheTacticalView );
12781278

12791279
// make the tactical display the full screen width and height
12801280
TheTacticalView->setWidth( TheDisplay->getWidth() );
12811281
TheTacticalView->setHeight( TheDisplay->getHeight() );
1282+
TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f);
12821283
}
1283-
TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f);
12841284

12851285
/** @todo this may be the wrong place to create the sidebar, but for now
12861286
this is where it lives */

GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DGameClient.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
3838

3939
// USER INCLUDES //////////////////////////////////////////////////////////////
40+
#include "Common/GlobalData.h"
4041
#include "GameClient/GameClient.h"
4142
#include "W3DDevice/GameClient/W3DParticleSys.h"
4243
#include "W3DDevice/GameClient/W3DDisplay.h"
@@ -117,7 +118,8 @@ class W3DGameClient : public GameClient
117118
virtual VideoPlayerInterface *createVideoPlayer( void ) { return NEW BinkVideoPlayer; }
118119
#endif
119120
/// factory for creating the TerrainVisual
120-
virtual TerrainVisual *createTerrainVisual( void ) { return NEW W3DTerrainVisual; }
121+
// TheSuperHackers @fix bobtista 31/01/2026 Return dummy in headless mode
122+
virtual TerrainVisual *createTerrainVisual( void ) { return TheGlobalData->m_headless ? NEW TerrainVisualDummy : NEW W3DTerrainVisual; }
121123

122124
/// factory for creating the snow manager
123125
virtual SnowManager *createSnowManager( void ) { return NEW W3DSnowManager; }

0 commit comments

Comments
 (0)