From adb98a47bac9beb98da864a940e511f0a4cabd97 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 23 Aug 2020 17:47:05 +0200 Subject: [PATCH] - restructured the main loop code so that the actual loop is in the common code. --- source/blood/src/blood.cpp | 117 ++++++++++---------------- source/blood/src/blood.h | 3 +- source/core/gamecontrol.cpp | 34 +++++++- source/core/gamestruct.h | 3 +- source/exhumed/src/exhumed.cpp | 2 +- source/exhumed/src/exhumed.h | 6 +- source/exhumed/src/gameloop.cpp | 114 +++++++++++--------------- source/games/duke/src/duke3d.h | 3 +- source/games/duke/src/game.cpp | 6 +- source/games/duke/src/gameloop.cpp | 99 ++++++++++------------ source/sw/src/game.cpp | 127 ++++++++++++----------------- source/sw/src/game.h | 3 +- 12 files changed, 231 insertions(+), 286 deletions(-) diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp index e95953365..4a5645866 100644 --- a/source/blood/src/blood.cpp +++ b/source/blood/src/blood.cpp @@ -68,8 +68,6 @@ void LocalKeys(void); void InitCheats(); bool bNoDemo = false; -bool bQuickStart = true; - char gUserMapFilename[BMAX_PATH]; @@ -714,13 +712,12 @@ static const char* actions[] = { "Toggle_Crouch", }; -static void app_init() +void GameInterface::app_init() { InitCheats(); buttonMap.SetButtons(actions, NUM_ACTIONS); memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS)); gGameOptions.nMonsterSettings = !userConfig.nomonsters; - bQuickStart = userConfig.nologo; ReadAllRFS(); HookReplaceFunctions(); @@ -854,7 +851,7 @@ static void drawBackground() netBroadcastMyLogoff(gQuitRequest == 2); } -static void commonTicker(bool &playvideo) +static void commonTicker() { if (TestBitString(gotpic, 2342)) { @@ -895,85 +892,61 @@ static void commonTicker(bool &playvideo) gQuitRequest = 0; gRestartGame = 0; - - if (gGameOptions.nGameType != 0) - { - playvideo = !bQuickStart; - } - else playvideo = false; // Don't switch to startup if we're already outside the game. if (gamestate == GS_LEVEL) gamestate = GS_STARTUP; } } -int GameInterface::app_main() +void GameInterface::RunGameFrame() { - - app_init(); - gamestate = GS_STARTUP; - bool playvideo = !bQuickStart; - while (true) + if (gamestate == GS_STARTUP) gameInit(); + + commonTicker(); + netGetPackets(); + handleevents(); + updatePauseStatus(); + D_ProcessEvents(); + ctrlGetInput(); + + switch (gamestate) { - try + default: + case GS_STARTUP: + if (userConfig.CommandMap.IsNotEmpty()) { - if (gamestate == GS_STARTUP) gameInit(); - - commonTicker(playvideo); - netGetPackets(); - handleevents(); - updatePauseStatus(); - D_ProcessEvents(); - ctrlGetInput(); - - switch (gamestate) + } + else + { + if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); + else { - default: - case GS_STARTUP: - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (playvideo) playlogos(); - else - { - gamestate = GS_MENUSCREEN; - M_StartControlPanel(false); - M_SetMenu(NAME_Mainmenu); - } - } - break; - - case GS_MENUSCREEN: - case GS_FULLCONSOLE: - drawBackground(); - break; - - case GS_INTRO: - case GS_INTERMISSION: - RunScreenJobFrame(); // This handles continuation through its completion callback. - break; - - case GS_LEVEL: - gameTicker(); - LocalKeys(); - break; - - case GS_FINALE: - gEndGameMgr.ProcessKeys(); - gEndGameMgr.Draw(); - break; + gamestate = GS_MENUSCREEN; + M_StartControlPanel(false); + M_SetMenu(NAME_Mainmenu); } + } + break; - videoNextPage(); - } - catch (CRecoverableError& err) - { - C_FullConsole(); - Printf(TEXTCOLOR_RED "%s\n", err.what()); - } + case GS_MENUSCREEN: + case GS_FULLCONSOLE: + drawBackground(); + break; + + case GS_INTRO: + case GS_INTERMISSION: + RunScreenJobFrame(); // This handles continuation through its completion callback. + break; + + case GS_LEVEL: + gameTicker(); + LocalKeys(); + break; + + case GS_FINALE: + gEndGameMgr.ProcessKeys(); + gEndGameMgr.Draw(); + break; } - return 0; } bool DemoRecordStatus(void) { diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h index 5bb7d6f28..41002d60e 100644 --- a/source/blood/src/blood.h +++ b/source/blood/src/blood.h @@ -107,7 +107,8 @@ void sndPlaySpecialMusicOrNothing(int nMusic); struct GameInterface : ::GameInterface { const char* Name() override { return "Blood"; } - int app_main() override; + void app_init() override; + void RunGameFrame() override; bool GenerateSavePic() override; void FreeGameData() override; FString statFPS() override; diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 5a9ed9eba..fcfd56132 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -117,6 +117,7 @@ void I_SetWindowTitle(const char* caption); void S_ParseSndInfo(); void I_DetectOS(void); void LoadScripts(); +void app_loop(); bool AppActive; @@ -817,6 +818,7 @@ int RunGame() { playername = userConfig.CommandName; } + GameTicRate = 30; CheckUserMap(); GPalette.Init(MAXPALOOKUPS + 2); // one slot for each translation, plus a separate one for the base palettes and the internal one TexMan.Init([]() {}, [](BuildInfo &) {}); @@ -836,16 +838,44 @@ int RunGame() if (enginePreInit()) { - I_FatalError("app_main: There was a problem initializing the Build engine: %s\n", engineerrstr); + I_FatalError("There was a problem initializing the Build engine: %s\n", engineerrstr); } auto exec = C_ParseCmdLineParams(nullptr); if (exec) exec->ExecCommands(); gamestate = GS_LEVEL; - return gi->app_main(); + gi->app_init(); + app_loop(); } +//--------------------------------------------------------------------------- +// +// The one and only main loop in the entire engine. Yay! +// +//--------------------------------------------------------------------------- + +void app_loop() +{ + gamestate = GS_STARTUP; + + while (true) + { + try + { + gi->RunGameFrame(); + videoNextPage(); + videoSetBrightness(0); // immediately reset this so that the value doesn't stick around in the backend. + } + catch (CRecoverableError& err) + { + C_FullConsole(); + Printf(TEXTCOLOR_RED "%s\n", err.what()); + } + } +} + + //========================================================================== // // diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index 5583ff43c..bfc975dcb 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -58,7 +58,8 @@ struct GameInterface virtual const char* Name() { return "$"; } virtual ~GameInterface() {} virtual bool GenerateSavePic() { return false; } - virtual int app_main() = 0; + virtual void app_init() = 0; + virtual void RunGameFrame() = 0; virtual void clearlocalinputstate() {} virtual void UpdateScreenSize() {} virtual void FreeGameData() {} diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp index 8c5b680e6..461bdca60 100644 --- a/source/exhumed/src/exhumed.cpp +++ b/source/exhumed/src/exhumed.cpp @@ -621,7 +621,7 @@ static const char* actions[] = -void InitGame() +void GameInterface::app_init() { int i; //int esi = 1; diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h index b8f766f80..3cecdfd70 100644 --- a/source/exhumed/src/exhumed.h +++ b/source/exhumed/src/exhumed.h @@ -134,7 +134,6 @@ void CheckKeys(); void CheckKeys2(); void GameTicker(); void InitLevel(int); -void InitGame(); void InitNewGame(); void startmainmenu(); @@ -294,8 +293,9 @@ struct SavegameHelper struct GameInterface : ::GameInterface { const char* Name() override { return "Exhumed"; } - int app_main() override; - bool GenerateSavePic() override; + void app_init() override; + void RunGameFrame() override; + bool GenerateSavePic() override; void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override; void MenuOpened() override; void MenuSound(EMenuSounds snd) override; diff --git a/source/exhumed/src/gameloop.cpp b/source/exhumed/src/gameloop.cpp index 8e7500c28..6e3902b4b 100644 --- a/source/exhumed/src/gameloop.cpp +++ b/source/exhumed/src/gameloop.cpp @@ -59,7 +59,6 @@ extern ClockTicks tclocks; void RunCinemaScene(int num); void GameMove(void); -void InitGame(); void DrawClock(); int32_t calc_smoothratio(ClockTicks totalclk, ClockTicks ototalclk); void DoTitle(CompletionFunc completion); @@ -262,78 +261,63 @@ void GameLoop() fps++; } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- -int GameInterface::app_main() +void GameInterface::RunGameFrame() { - InitGame(); - gamestate = GS_STARTUP; - - while (true) + again: + try { - try + HandleAsync(); + updatePauseStatus(); + D_ProcessEvents(); + CheckProgression(); + switch (gamestate) { - HandleAsync(); - updatePauseStatus(); - D_ProcessEvents(); - CheckProgression(); - switch (gamestate) - { - default: - case GS_STARTUP: - totalclock = 0; - ototalclock = 0; - GameAction = -1; - EndLevel = false; - - if (userConfig.CommandMap.IsNotEmpty()) - { - auto map = FindMapByName(userConfig.CommandMap); - if (map) GameAction = map->levelNumber; - userConfig.CommandMap = ""; - continue; - } - else - { - DoTitle([](bool) { startmainmenu(); }); - } - break; - - case GS_MENUSCREEN: - case GS_FULLCONSOLE: - drawmenubackground(); - break; - - case GS_LEVEL: - GameLoop(); - GameDisplay(); - break; - - case GS_INTERMISSION: - case GS_INTRO: - RunScreenJobFrame(); // This handles continuation through its completion callback. - break; - - } - videoNextPage(); - } - catch (CRecoverableError& err) - { - // Clear all progression sensitive variables here. + default: + case GS_STARTUP: + totalclock = 0; + ototalclock = 0; GameAction = -1; EndLevel = false; - C_FullConsole(); - Printf(TEXTCOLOR_RED "%s\n", err.what()); + + if (userConfig.CommandMap.IsNotEmpty()) + { + auto map = FindMapByName(userConfig.CommandMap); + if (map) GameAction = map->levelNumber; + userConfig.CommandMap = ""; + goto again; + } + else + { + DoTitle([](bool) { startmainmenu(); }); + } + break; + + case GS_MENUSCREEN: + case GS_FULLCONSOLE: + drawmenubackground(); + break; + + case GS_LEVEL: + GameLoop(); + GameDisplay(); + break; + + case GS_INTERMISSION: + case GS_INTRO: + RunScreenJobFrame(); // This handles continuation through its completion callback. + break; + } } + catch (CRecoverableError&) + { + // Clear all progression sensitive variables here. + GameAction = -1; + EndLevel = false; + throw; + } + } - - - - END_PS_NS diff --git a/source/games/duke/src/duke3d.h b/source/games/duke/src/duke3d.h index c76de2be7..9d2dfbfe8 100644 --- a/source/games/duke/src/duke3d.h +++ b/source/games/duke/src/duke3d.h @@ -34,7 +34,8 @@ extern FFont* DigiFont; struct GameInterface : public ::GameInterface { const char* Name() override { return "Duke"; } - int app_main() override; + void app_init() override; + void RunGameFrame() override; void clearlocalinputstate() override; bool GenerateSavePic() override; void PlayHudSound() override; diff --git a/source/games/duke/src/game.cpp b/source/games/duke/src/game.cpp index f53746dd1..025e8fda2 100644 --- a/source/games/duke/src/game.cpp +++ b/source/games/duke/src/game.cpp @@ -423,14 +423,12 @@ static void Startup(void) // //--------------------------------------------------------------------------- -void app_loop(); -int GameInterface::app_main() +void GameInterface::app_init() { Startup(); enginePostInit(); videoInit(); - app_loop(); - return 0; + enginecompatibility_mode = ENGINECOMPATIBILITY_19961112;//bVanilla; } diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index 8dd74fce8..add4f7458 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -399,77 +399,60 @@ void startmainmenu() // //--------------------------------------------------------------------------- -void app_loop() +void GameInterface::RunGameFrame() { - gamestate = GS_STARTUP; - enginecompatibility_mode = ENGINECOMPATIBILITY_19961112;//bVanilla; - - while (true) + handleevents(); + updatePauseStatus(); + D_ProcessEvents(); + switch (gamestate) { - try + default: + case GS_STARTUP: + totalclock = 0; + ototalclock = 0; + lockclock = 0; + + ps[myconnectindex].ftq = 0; + + if (userConfig.CommandMap.IsNotEmpty()) { - handleevents(); - updatePauseStatus(); - D_ProcessEvents(); - switch (gamestate) + auto maprecord = FindMapByName(userConfig.CommandMap); + userConfig.CommandMap = ""; + if (maprecord) { - default: - case GS_STARTUP: - totalclock = 0; - ototalclock = 0; - lockclock = 0; + ud.m_respawn_monsters = ud.m_player_skill == 4; - ps[myconnectindex].ftq = 0; - - if (userConfig.CommandMap.IsNotEmpty()) + for (int i = 0; i != -1; i = connectpoint2[i]) { - auto maprecord = FindMapByName(userConfig.CommandMap); - userConfig.CommandMap = ""; - if (maprecord) - { - ud.m_respawn_monsters = ud.m_player_skill == 4; - - for (int i = 0; i != -1; i = connectpoint2[i]) - { - resetweapons(i); - resetinventory(i); - } - startnewgame(maprecord, /*userConfig.skill*/2); - } + resetweapons(i); + resetinventory(i); } - else - { - fi.ShowLogo([](bool) { startmainmenu(); }); - } - break; - - case GS_MENUSCREEN: - case GS_FULLCONSOLE: - drawbackground(); - break; - - case GS_LEVEL: - if (GameTicker()) gamestate = GS_STARTUP; - else videoSetBrightness(thunder_brightness); - break; - - case GS_INTERMISSION: - case GS_INTRO: - RunScreenJobFrame(); // This handles continuation through its completion callback. - break; - + startnewgame(maprecord, /*userConfig.skill*/2); } - videoNextPage(); - videoSetBrightness(0); // immediately reset this so that the value doesn't stick around in the backend. } - catch (CRecoverableError& err) + else { - C_FullConsole(); - Printf(TEXTCOLOR_RED "%s\n", err.what()); + fi.ShowLogo([](bool) { startmainmenu(); }); } + break; + + case GS_MENUSCREEN: + case GS_FULLCONSOLE: + drawbackground(); + break; + + case GS_LEVEL: + if (GameTicker()) gamestate = GS_STARTUP; + else videoSetBrightness(thunder_brightness); + break; + + case GS_INTERMISSION: + case GS_INTRO: + RunScreenJobFrame(); // This handles continuation through its completion callback. + break; + } } - END_DUKE_NS diff --git a/source/sw/src/game.cpp b/source/sw/src/game.cpp index 50de63a4e..8f9e48da5 100644 --- a/source/sw/src/game.cpp +++ b/source/sw/src/game.cpp @@ -213,8 +213,9 @@ static const char* actions[] = { }; -bool InitGame() +void GameInterface::app_init() { + GameTicRate = 40; InitCheats(); buttonMap.SetButtons(actions, NUM_ACTIONS); automapping = 1; @@ -284,7 +285,6 @@ bool InitGame() enginePostInit(); videoInit(); InitFX(); - return true; } //--------------------------------------------------------------------------- @@ -804,89 +804,62 @@ void GameTicker(void) } } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- -int32_t GameInterface::app_main() +void GameInterface::RunGameFrame() { - InitGame(); - gamestate = GS_STARTUP; - - - while (true) + try { - try - { - // if the menu initiazed a new game or loaded a savegame, switch to play mode. - if (SavegameLoaded || NextLevel) gamestate = GS_LEVEL; + // if the menu initiazed a new game or loaded a savegame, switch to play mode. + if (SavegameLoaded || NextLevel) gamestate = GS_LEVEL; - handleevents(); - updatePauseStatus(); - D_ProcessEvents(); - DoUpdateSounds(); - switch (gamestate) - { - default: - case GS_STARTUP: - totalclock = 0; - ototalclock = 0; - - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (!userConfig.nologo) Logo([](bool) { StartMenu(); }); - else StartMenu(); - } - break; - - case GS_MENUSCREEN: - case GS_FULLCONSOLE: - DrawMenuLevelScreen(); - break; - - case GS_LEVEL: - GameTicker(); - break; - - case GS_INTERMISSION: - case GS_INTRO: - RunScreenJobFrame(); // This handles continuation through its completion callback. - break; - - } - videoNextPage(); - } - catch (CRecoverableError& err) - { - TerminateLevel(); - NextLevel = nullptr; - SavegameLoaded = false; - ExitLevel = false; - FinishAnim = 0; - C_FullConsole(); - Printf(TEXTCOLOR_RED "%s\n", err.what()); - } - } - -#if 0 - while (true) - { handleevents(); - C_RunDelayedCommands(); + updatePauseStatus(); + D_ProcessEvents(); + DoUpdateSounds(); + switch (gamestate) + { + default: + case GS_STARTUP: + totalclock = 0; + ototalclock = 0; - NewLevel(); + if (userConfig.CommandMap.IsNotEmpty()) + { + } + else + { + if (!userConfig.nologo) Logo([](bool) { StartMenu(); }); + else StartMenu(); + } + break; + + case GS_MENUSCREEN: + case GS_FULLCONSOLE: + DrawMenuLevelScreen(); + break; + + case GS_LEVEL: + GameTicker(); + break; + + case GS_INTERMISSION: + case GS_INTRO: + RunScreenJobFrame(); // This handles continuation through its completion callback. + break; + + } + } + catch (CRecoverableError&) + { + // Make sure we do not leave the game in an unstable state + TerminateLevel(); + NextLevel = nullptr; + SavegameLoaded = false; + ExitLevel = false; + FinishAnim = 0; + throw; } - //SybexScreen(); - throw CExitEvent(0); -#endif - - return 0; } //--------------------------------------------------------------------------- diff --git a/source/sw/src/game.h b/source/sw/src/game.h index 0ee216f0d..9c0ef9ea6 100644 --- a/source/sw/src/game.h +++ b/source/sw/src/game.h @@ -2364,7 +2364,8 @@ extern short Bunny_Count; struct GameInterface : ::GameInterface { const char* Name() override { return "ShadowWarrior"; } - int app_main() override; + void app_init() override; + void RunGameFrame() override; void FreeGameData() override; bool GenerateSavePic() override; void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override;