Skip to content

Conversation

@bobtista
Copy link

@bobtista bobtista commented Jan 19, 2026

Depends on #2137 (ParticleSystem use-after-free fix) to avoid crash during headless replay playback.

Summary

  • Add GhostObjectManagerDummy class that stubs out ghost object operations for headless mode
  • Set it up to be instantiated when TheGlobalData->m_headless is true
  • Based on Helmut's work in [ZH] Implement Headless Mode #651

Testing

  • Run with -headless -replay <file> and verify no crashes (cherry pick the particlesystem fix first)

Todo

  • Replicate to Generals

Notes

-I tried to add InGameUIDummy too, but it broke things.
-The GhostObjectManagerDummy overrides only the operational methods (reset, addGhostObject, removeGhostObject, etc.) while inheriting crc/xfer/loadPostProcess from the base class to maintain save/load compatibility.

@greptile-apps
Copy link

greptile-apps bot commented Jan 19, 2026

Greptile Summary

Added GhostObjectManagerDummy class to stub out ghost object operations for headless mode, enabling replay playback without graphics rendering.

  • Created GhostObjectManagerDummy that inherits from GhostObjectManager and overrides operational methods (reset, addGhostObject, removeGhostObject, etc.) to do nothing
  • Modified W3DGameLogic::createGhostObjectManager() to instantiate GhostObjectManagerDummy when TheGlobalData->m_headless is true
  • The dummy implementation correctly inherits crc/xfer/loadPostProcess from base class to maintain save/load compatibility (avoiding the W3DGhostObjectManager implementation that expects non-null ghost objects)
  • All callers of addGhostObject properly handle nullptr returns, making the stub implementation safe
  • Uses nullptr (not NULL) following the project's C++ coding standard

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is architecturally sound - GhostObjectManagerDummy correctly stubs out ghost object operations for headless mode while preserving save/load compatibility by inheriting the base class serialization methods. All callers properly handle nullptr returns from addGhostObject. The code follows existing patterns from PR [ZH] Implement Headless Mode #651 and has been tested with headless replay playback. The use of nullptr instead of NULL follows the custom rule.
  • No files require special attention

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Include/GameLogic/GhostObject.h Added GhostObjectManagerDummy class that stubs out ghost object operations for headless mode while preserving save/load compatibility
GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameLogic/W3DGameLogic.h Modified createGhostObjectManager to return GhostObjectManagerDummy when in headless mode, otherwise W3DGhostObjectManager

Sequence Diagram

sequenceDiagram
    participant GameLogic
    participant W3DGameLogic
    participant TheGlobalData
    participant GhostObjectManagerDummy
    participant W3DGhostObjectManager
    participant PartitionData
    
    GameLogic->>W3DGameLogic: createGhostObjectManager()
    W3DGameLogic->>TheGlobalData: check m_headless flag
    
    alt Headless Mode
        W3DGameLogic->>GhostObjectManagerDummy: NEW GhostObjectManagerDummy
        GhostObjectManagerDummy-->>GameLogic: return (as GhostObjectManager*)
        Note over GhostObjectManagerDummy: All operations stubbed out<br/>Returns nullptr from addGhostObject
    else Normal Mode
        W3DGameLogic->>W3DGhostObjectManager: NEW W3DGhostObjectManager
        W3DGhostObjectManager-->>GameLogic: return
        Note over W3DGhostObjectManager: Full implementation<br/>Creates W3DGhostObject instances
    end
    
    Note over GameLogic: Later during gameplay...
    PartitionData->>GameLogic: TheGhostObjectManager->addGhostObject(obj, pd)
    
    alt Headless Mode
        GameLogic->>GhostObjectManagerDummy: addGhostObject(obj, pd)
        GhostObjectManagerDummy-->>PartitionData: return nullptr
        Note over PartitionData: m_ghostObject = nullptr<br/>All subsequent uses check for null
    else Normal Mode
        GameLogic->>W3DGhostObjectManager: addGhostObject(obj, pd)
        W3DGhostObjectManager-->>PartitionData: return W3DGhostObject*
        Note over PartitionData: m_ghostObject = valid pointer<br/>Used for fog-of-war rendering
    end
Loading

@bobtista bobtista force-pushed the bobtista/headless-dummy-classes branch from f48524e to 11152fa Compare January 19, 2026 03:14
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

virtual TerrainLogic *createTerrainLogic( void ) { return NEW W3DTerrainLogic; };
virtual GhostObjectManager *createGhostObjectManager(void) { return NEW W3DGhostObjectManager; }
// TheSuperHackers @feature bobtista 18/01/2026 Use dummy for headless mode
virtual GhostObjectManager *createGhostObjectManager(void) { return TheGlobalData->m_headless ? static_cast<GhostObjectManager*>(NEW GhostObjectManagerDummy) : NEW W3DGhostObjectManager; }
Copy link

Choose a reason for hiding this comment

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

style: unnecessary static_cast since GhostObjectManagerDummy already inherits from GhostObjectManager

Suggested change
virtual GhostObjectManager *createGhostObjectManager(void) { return TheGlobalData->m_headless ? static_cast<GhostObjectManager*>(NEW GhostObjectManagerDummy) : NEW W3DGhostObjectManager; }
virtual GhostObjectManager *createGhostObjectManager(void) { return TheGlobalData->m_headless ? NEW GhostObjectManagerDummy : NEW W3DGhostObjectManager; }

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameLogic/W3DGameLogic.h
Line: 64:64

Comment:
**style:** unnecessary `static_cast` since `GhostObjectManagerDummy` already inherits from `GhostObjectManager`

```suggestion
	virtual GhostObjectManager *createGhostObjectManager(void) { return TheGlobalData->m_headless ? NEW GhostObjectManagerDummy : NEW W3DGhostObjectManager; }
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Author

Choose a reason for hiding this comment

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

I think this is required for VC6, it failed to build without it

@bobtista bobtista changed the title feat(headless): Add GhostObjectManagerDummy and InGameUIDummy for headless mode feat(headless): Add InGameUIDummy for headless mode Jan 19, 2026
@bobtista bobtista force-pushed the bobtista/headless-dummy-classes branch from 52aa47b to 393f23b Compare January 19, 2026 07:24
@bobtista bobtista changed the title feat(headless): Add InGameUIDummy for headless mode feat(headless): Add GhostObjectManagerDummy for headless mode Jan 19, 2026
@helmutbuhler
Copy link

Did you add this for performance reasons, or to reduce headless checks? I don't see any removed headless checks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants