Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
765d92b
Implement graceful exit for Alt+F4 and window close events
githubawn Feb 20, 2026
4fda496
Implement graceful exit during loading and movie screens
githubawn Feb 21, 2026
4465409
Backport graceful exit logic from GeneralsMD
githubawn Feb 22, 2026
036ed84
Implemented feedback
githubawn Feb 22, 2026
27abc6b
Merge branch 'main' into fix/graceful-exit-logic
githubawn Feb 24, 2026
6b28aed
Apply suggestion from @greptile-apps[bot]
githubawn Feb 24, 2026
6f2b0b8
refine graceful exit logic and apply suggestions
githubawn Feb 27, 2026
c655da4
added tryStartNewGame to avoid long catch
githubawn Mar 3, 2026
4b7a872
added comment and removed blank link
githubawn Mar 3, 2026
c6ee106
Added isquitmenuvisible as check
githubawn Mar 3, 2026
5249bf9
Make m_quitToDesktopAfterMatch private.
githubawn Mar 18, 2026
e61b8cd
Merge branch 'main' into fix/graceful-exit-logic
githubawn Mar 18, 2026
f8e6218
fix upstream gamelogic
githubawn Mar 18, 2026
9058ad7
added skirmish guard back
githubawn Mar 18, 2026
b7ded01
added Missing !getQuitting() guard in Generals WM_CLOSE
githubawn Mar 18, 2026
f5133f3
implement review feedback
githubawn Mar 18, 2026
9025859
small changes to comments
githubawn Mar 18, 2026
5e2f096
fixes frameDecompress() being called on a potentially non-ready frame
githubawn Mar 18, 2026
573f408
simplified CommandXlat
githubawn Mar 22, 2026
abf2b36
Potential null dereference in WM_QUERYENDSESSION else branch
githubawn Mar 22, 2026
4030b7d
move variable and include to more logical place
githubawn Mar 24, 2026
d2b15e6
Remove TheMessageStream->propagateMessages(); from LoadScreen.cpp
githubawn Mar 29, 2026
0608194
implemented review feedback
githubawn Apr 11, 2026
d2fb136
implemented review feedback
githubawn Apr 12, 2026
3493355
added guard against accidental key combo
githubawn Apr 18, 2026
0f77c06
fixed double check
githubawn Apr 18, 2026
60b73e2
added comment
githubawn Apr 20, 2026
99de16a
updated WM_CLOSE comment
githubawn Apr 23, 2026
aa566ce
moved DEMO_INSTANT_QUIT out of debug in Generals
githubawn Apr 23, 2026
a210eb8
Remove unused Bool force parameter from GameLogic::quit
githubawn Apr 24, 2026
1256c88
removed isAltF4
githubawn Apr 24, 2026
071cb9f
Rename canAddMessage to isLocalPlayerReady with safe local player val…
githubawn Apr 24, 2026
df5fa09
remove unnecessary guard
githubawn Apr 26, 2026
2b607ea
Add guard to TheGameEngine to prevent possible crashes during shutdown
githubawn Apr 26, 2026
9a101d7
hardened WM_QUERYENDSESSION
githubawn Apr 26, 2026
caae0fa
Added null guard to canOpenQuitMenu TheGameLogic
githubawn Apr 26, 2026
eedcde5
fix multiplayer map abort
githubawn Apr 26, 2026
f23487e
Merge branch 'main' into fix/graceful-exit-logic
githubawn Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 24 additions & 23 deletions Core/GameEngine/Source/GameClient/GUI/LoadScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "Common/GameEngine.h"
#include "Common/GameLOD.h"
#include "Common/GameState.h"
#include "Common/MessageStream.h"
#include "Common/MultiplayerSettings.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
Expand All @@ -68,6 +69,7 @@
#include "GameClient/Display.h"
#include "GameClient/GadgetProgressBar.h"
#include "GameClient/GadgetStaticText.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GameWindowTransitions.h"
Expand Down Expand Up @@ -157,7 +159,8 @@ LoadScreen::~LoadScreen()
void LoadScreen::update( Int percent )
{
TheGameEngine->serviceWindowsOS();
if (TheGameEngine->getQuitting())
TheMessageStream->propagateMessages();
if (TheGameEngine->getQuitting() || (TheGameLogic && TheGameLogic->isQuitToDesktopRequested()))
return; //don't bother with any of this if the player is exiting game.

TheWindowManager->update();
Expand Down Expand Up @@ -539,20 +542,11 @@ void SinglePlayerLoadScreen::init( GameInfo *game )
Int shiftedPercent = -FRAME_FUDGE_ADD + 1;
while (m_videoStream->frameIndex() < m_videoStream->frameCount() - 1 )
{
// TheSuperHackers @feature User can now skip video by pressing ESC
if (TheKeyboard)
if (GameClient::isMovieAbortRequested())
{
TheKeyboard->UPDATE();
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
{
io->setUsed();
break;
}
break;
}

TheGameEngine->serviceWindowsOS();

if(!m_videoStream->isFrameReady())
{
Sleep(1);
Expand Down Expand Up @@ -634,6 +628,11 @@ void SinglePlayerLoadScreen::init( GameInfo *game )
fudgeFactor = 30 * ((currTime - begin)/ INT_TO_REAL(delay ));
GadgetProgressBarSetProgress(m_progressBar, fudgeFactor);

if (GameClient::isMovieAbortRequested())
{
break;
}

TheWindowManager->update();
TheDisplay->draw();
Sleep(100);
Expand Down Expand Up @@ -1054,20 +1053,11 @@ void ChallengeLoadScreen::init( GameInfo *game )
Int shiftedPercent = -FRAME_FUDGE_ADD + 1;
while (m_videoStream->frameIndex() < m_videoStream->frameCount() - 1 )
{
// TheSuperHackers @feature User can now skip video by pressing ESC
if (TheKeyboard)
if (GameClient::isMovieAbortRequested())
{
TheKeyboard->UPDATE();
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
{
io->setUsed();
break;
}
break;
}

TheGameEngine->serviceWindowsOS();

if(!m_videoStream->isFrameReady())
{
Sleep(1);
Expand Down Expand Up @@ -1109,7 +1099,13 @@ void ChallengeLoadScreen::init( GameInfo *game )
// if we're min speced
m_videoStream->frameGoto(m_videoStream->frameCount()); // zero based
while(!m_videoStream->isFrameReady())
{
if (GameClient::isMovieAbortRequested())
{
break;
Comment thread
githubawn marked this conversation as resolved.
Outdated
}
Sleep(1);
}
m_videoStream->frameDecompress();
m_videoStream->frameRender(m_videoBuffer);
Comment thread
greptile-apps[bot] marked this conversation as resolved.
if(m_videoBuffer)
Expand All @@ -1126,6 +1122,11 @@ void ChallengeLoadScreen::init( GameInfo *game )
fudgeFactor = 30 * ((currTime - begin)/ INT_TO_REAL(delay ));
GadgetProgressBarSetProgress(m_progressBar, fudgeFactor);

if (GameClient::isMovieAbortRequested())
{
break;
}

TheWindowManager->update();
TheDisplay->draw();
Sleep(100);
Expand Down
2 changes: 1 addition & 1 deletion Generals/Code/GameEngine/Include/Common/MessageStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class GameMessage : public MemoryPoolObject
MSG_META_TOGGLE_PAUSE_ALT, ///< TheSuperHackers @feature Toggle game pause (alternative mapping)
MSG_META_STEP_FRAME, ///< TheSuperHackers @feature Step one frame
MSG_META_STEP_FRAME_ALT, ///< TheSuperHackers @feature Step one frame (alternative mapping)
MSG_META_DEMO_INSTANT_QUIT, ///< bail out of game immediately
Comment thread
githubawn marked this conversation as resolved.


// META items that are really for debug/demo/development use only...
Expand All @@ -289,7 +290,6 @@ class GameMessage : public MemoryPoolObject
MSG_META_DEMO_LOD_INCREASE, ///< increase LOD by 1
MSG_META_DEMO_TOGGLE_ZOOM_LOCK, ///< Toggle the camera zoom lock on/off
MSG_META_DEMO_PLAY_CAMEO_MOVIE, ///< Play a movie in the cameo spot
MSG_META_DEMO_INSTANT_QUIT, ///< bail out of game immediately
MSG_META_DEMO_TOGGLE_SPECIAL_POWER_DELAYS, ///< Toggle special power delays on/off
MSG_META_DEMO_BATTLE_CRY, ///< battle cry
MSG_META_DEMO_SWITCH_TEAMS, ///< switch local control to another team
Expand Down
2 changes: 2 additions & 0 deletions Generals/Code/GameEngine/Include/GameClient/GameClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class GameClient : public SubsystemInterface,
UnsignedInt getRenderedObjectCount() const { return m_renderedObjectCount; }
void incrementRenderedObjectCount() { m_renderedObjectCount++; }

static Bool isMovieAbortRequested();

protected:

// snapshot methods
Expand Down
8 changes: 7 additions & 1 deletion Generals/Code/GameEngine/Include/GameLogic/GameLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
ObjectID allocateObjectID(); ///< Returns a new unique object id

// super hack
void startNewGame( Bool loadSaveGame );
void startNewGame( Bool saveGame );
Comment thread
githubawn marked this conversation as resolved.
Outdated
void loadMapINI( AsciiString mapName );

Comment thread
greptile-apps[bot] marked this conversation as resolved.
void updateLoadProgress( Int progress );
Expand Down Expand Up @@ -206,6 +206,7 @@ class GameLogic : public SubsystemInterface, public Snapshot
UnsignedInt getFrameObjectsChangedTriggerAreas() {return m_frameObjectsChangedTriggerAreas;}

void exitGame();
void quit(Bool toDesktop, Bool force = FALSE);
void clearGameData(Bool showScoreScreen = TRUE); ///< Clear the game data
void closeWindows();

Expand Down Expand Up @@ -253,6 +254,8 @@ class GameLogic : public SubsystemInterface, public Snapshot
// this should be called only by UpdateModule, thanks.
void friend_awakenUpdateModule(Object* obj, UpdateModulePtr update, UnsignedInt whenToWakeUp);

Bool isQuitToDesktopRequested() const { return m_quitToDesktopAfterMatch; }

protected:

// snapshot methods
Expand All @@ -262,6 +265,9 @@ class GameLogic : public SubsystemInterface, public Snapshot

private:

void tryStartNewGame( Bool loadSaveGame );
Bool m_quitToDesktopAfterMatch;

void updateDisplayBusyState();

void pauseGameLogic(Bool paused);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,9 @@ void destroyQuitMenu()
*/
static void exitQuitMenu()
{
TheGameLogic->quit(FALSE);
// destroy the quit menu
destroyQuitMenu();

// clear out all the game data
if ( TheGameLogic->isInMultiplayerGame() && !TheGameLogic->isInSkirmishGame() && !TheGameInfo->isSandbox() )
{
GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT);
msg->appendBooleanArgument(TRUE);
}
TheGameLogic->exitGame();
// TheGameLogic->clearGameData();
// display the menu on top of the shell stack
// TheShell->showShell();

// this will trigger an exit
// TheGameEngine->setQuitting( TRUE );
TheInGameUI->setClientQuiet( TRUE );
}
static void noExitQuitMenu()
{
Expand All @@ -162,20 +148,9 @@ static void noExitQuitMenu()

static void quitToDesktopQuitMenu()
{
TheGameLogic->quit(TRUE);
// destroy the quit menu
destroyQuitMenu();

if (TheGameLogic->isInGame())
{
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
{
TheRecorder->stopRecording();
}
TheGameLogic->clearGameData();
}
TheGameEngine->setQuitting(TRUE);
TheInGameUI->setClientQuiet( TRUE );

}

static void surrenderQuitMenu()
Expand Down
31 changes: 31 additions & 0 deletions Generals/Code/GameEngine/Source/GameClient/GameClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,11 @@ void GameClient::update()
Int beginTime = timeGetTime();
while(beginTime + 4000 > timeGetTime() )
{
if (GameClient::isMovieAbortRequested())
{
break;
}

TheWindowManager->update();
// redraw all views, update the GUI
TheDisplay->draw();
Expand Down Expand Up @@ -752,6 +757,32 @@ void GameClient::updateHeadless()
TheParticleSystemManager->update();
}

Bool GameClient::isMovieAbortRequested()
Comment thread
githubawn marked this conversation as resolved.
{
// TheSuperHackers @feature User can skip video by pressing ESC
if (TheKeyboard)
{
TheKeyboard->UPDATE();
KeyboardIO *io = TheKeyboard->findKey(KEY_ESC, KeyboardIO::STATUS_UNUSED);
if (io && BitIsSet(io->state, KEY_STATE_DOWN))
{
io->setUsed();
return TRUE;
}
}

// TheSuperHackers @feature Service OS for Window Close / Alt-F4 events
TheGameEngine->serviceWindowsOS();
TheMessageStream->propagateMessages();

if (TheGameEngine->getQuitting() || (TheGameLogic && TheGameLogic->isQuitToDesktopRequested()))
{
return TRUE;
}

return FALSE;
}

/** -----------------------------------------------------------------------------------------------
* Call the given callback function for each object contained within the given region.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3699,27 +3699,20 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
break;

}


#if defined(RTS_DEBUG)
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
//------------------------------------------------------------------------- BEGIN DEMO MESSAGES
//------------------------------------------------------------------------------- DEMO MESSAGES
//-----------------------------------------------------------------------------------------

case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
if (TheGameLogic->isInGame())
{
Bool force = FALSE;
if (msg->getArgumentCount() > 0)
{
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
{
TheRecorder->stopRecording();
}
TheGameLogic->clearGameData();
force = msg->getArgument(0)->boolean;
}
TheGameEngine->setQuitting(TRUE);
TheGameLogic->quit(TRUE, force);
disp = DESTROY_MESSAGE;
break;
}
Comment thread
githubawn marked this conversation as resolved.

#if defined(RTS_DEBUG)
//------------------------------------------------------------------------------- DEMO MESSAGES
//-----------------------------------------------------------------------------------------
case GameMessage::MSG_META_DEMO_SWITCH_TEAMS:
Expand Down Expand Up @@ -5055,6 +5048,7 @@ static Bool isSystemMessage( const GameMessage *msg )
case GameMessage::MSG_LOGIC_CRC:
case GameMessage::MSG_SET_REPLAY_CAMERA:
case GameMessage::MSG_FRAME_TICK:
case GameMessage::MSG_META_DEMO_INSTANT_QUIT:
return TRUE;
}
return FALSE;
Expand Down
Loading
Loading