From eae73ddd7a006ee6de9ffb01f3ec5f8e5fdc6d85 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20Verschelde?= Date: Wed, 4 Jan 2017 18:28:04 +0100 Subject: [PATCH] Console: Return gracefully when loading missing level For this, implemented Folders::file_exists to check for the existence of a given file, so that we can exit gracefully before raising a FileNotFound exception which then closes the game. The console and stderr both display an error message. Part of #23. --- Source/Devtools/ConsoleCmds.cpp | 9 ++++++++- Source/Game.hpp | 4 ++-- Source/GameTick.cpp | 29 +++++++++++++++++------------ Source/Menu/Menu.cpp | 6 +++--- Source/Utils/Folders.cpp | 12 ++++++++++++ Source/Utils/Folders.hpp | 2 ++ 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/Source/Devtools/ConsoleCmds.cpp b/Source/Devtools/ConsoleCmds.cpp index b9aabcc..b11e12a 100644 --- a/Source/Devtools/ConsoleCmds.cpp +++ b/Source/Devtools/ConsoleCmds.cpp @@ -163,7 +163,14 @@ void ch_quit(const char *) void ch_map(const char *args) { - Loadlevel(args); + if (!LoadLevel(args)) { + // FIXME: Reduce code duplication with GameTick (should come from a Console class) + for (int k = 14; k >= 1; k--) { + consoletext[k] = consoletext[k - 1]; + } + consoletext[0] = std::string("Could not load the requested level '") + args + "', aborting."; + consoleselected = 0; + } whichlevel = -2; campaign = 0; } diff --git a/Source/Game.hpp b/Source/Game.hpp index 17e188a..159cbe7 100644 --- a/Source/Game.hpp +++ b/Source/Game.hpp @@ -144,8 +144,8 @@ void LoadingScreen(); int DrawGLScene(StereoSide side); void playdialoguescenesound(); int findClosestPlayer(); -void Loadlevel(int which); -void Loadlevel(const std::string& name, bool tutorial = false); +bool LoadLevel(int which); +bool LoadLevel(const std::string& name, bool tutorial = false); void Tick(); void TickOnce(); void TickOnceAfter(); diff --git a/Source/GameTick.cpp b/Source/GameTick.cpp index ed22ee2..58fb64a 100644 --- a/Source/GameTick.cpp +++ b/Source/GameTick.cpp @@ -491,24 +491,30 @@ void Setenvironment(int which) texdetail = temptexdetail; } -void Game::Loadlevel(int which) +bool Game::LoadLevel(int which) { stealthloading = 0; whichlevel = which; if (which == -1) { - Loadlevel("tutorial", true); + return LoadLevel("tutorial", true); } else if (which >= 0 && which <= 15) { char buf[32]; snprintf(buf, 32, "map%d", which + 1); // challenges - Loadlevel(buf); + return LoadLevel(buf); } else { - Loadlevel("mapsave"); + return LoadLevel("mapsave"); } } -void Game::Loadlevel(const std::string& name, bool tutorial) +bool Game::LoadLevel(const std::string& name, bool tutorial) { + const std::string level_path = Folders::getResourcePath("Maps/" + name); + if (!Folders::file_exists(level_path)) { + perror(std::string("LoadLevel: Could not open file '" + level_path).c_str()); + return false; + } + int indemo; // FIXME this should be removed int templength; float lamefloat; @@ -542,7 +548,7 @@ void Game::Loadlevel(const std::string& name, bool tutorial) int mapvers; FILE *tfile; errno = 0; - tfile = Folders::openMandatoryFile( Folders::getResourcePath("Maps/"+name), "rb" ); + tfile = Folders::openMandatoryFile(level_path, "rb"); pause_sound(stream_firesound); scoreadded = 0; @@ -929,6 +935,8 @@ void Game::Loadlevel(const std::string& name, bool tutorial) leveltime = 0; wonleveltime = 0; visibleloading = false; + + return true; } void doDevKeys() @@ -4484,11 +4492,8 @@ void Game::TickOnceAfter() if (!Person::players[0]->dead && targetlevel != whichlevel) startbonustotal = bonustotal; - if (Person::players[0]->dead) - Loadlevel(whichlevel); - else - Loadlevel(targetlevel); + LoadLevel(targetlevel); fireSound(); loading = 3; @@ -4499,7 +4504,7 @@ void Game::TickOnceAfter() fireSound(firestartsound); - Loadlevel(campaignlevels[Account::active().getCampaignChoicesMade()].mapname.c_str()); + LoadLevel(campaignlevels[Account::active().getCampaignChoicesMade()].mapname.c_str()); fireSound(); @@ -4559,7 +4564,7 @@ void Game::TickOnceAfter() actuallevel = campaignlevels[actuallevel].nextlevel.front(); visibleloading = true; stillloading = 1; - Loadlevel(campaignlevels[actuallevel].mapname.c_str()); + LoadLevel(campaignlevels[actuallevel].mapname.c_str()); campaign = 1; mainmenu = 0; gameon = 1; diff --git a/Source/Menu/Menu.cpp b/Source/Menu/Menu.cpp index 27582aa..91dd2ed 100644 --- a/Source/Menu/Menu.cpp +++ b/Source/Menu/Menu.cpp @@ -715,7 +715,7 @@ void Menu::Tick() actuallevel = (Account::active().getCampaignChoicesMade() > 0 ? campaignlevels[Account::active().getCampaignChoicesMade() - 1].nextlevel[whichchoice] : 0); visibleloading = true; stillloading = 1; - Loadlevel(campaignlevels[actuallevel].mapname.c_str()); + LoadLevel(campaignlevels[actuallevel].mapname.c_str()); campaign = 1; mainmenu = 0; gameon = 1; @@ -733,7 +733,7 @@ void Menu::Tick() } else { LoadStuff(); } - Loadlevel(-1); + LoadLevel(-1); mainmenu = 0; gameon = 1; @@ -820,7 +820,7 @@ void Menu::Tick() } else { LoadStuff(); } - Loadlevel(selected); + LoadLevel(selected); campaign = 0; mainmenu = 0; diff --git a/Source/Utils/Folders.cpp b/Source/Utils/Folders.cpp index 8f2bc18..3fa0402 100644 --- a/Source/Utils/Folders.cpp +++ b/Source/Utils/Folders.cpp @@ -133,3 +133,15 @@ FILE* Folders::openMandatoryFile(const std::string& filename, const char* mode) } return tfile; } + +bool Folders::file_exists(const std::string& filepath) +{ + FILE* file; + file = fopen(filepath.c_str(), "rb"); + if (file == NULL) { + return false; + } else { + fclose(file); + return true; + } +} diff --git a/Source/Utils/Folders.hpp b/Source/Utils/Folders.hpp index 9ecf9e0..d812d10 100644 --- a/Source/Utils/Folders.hpp +++ b/Source/Utils/Folders.hpp @@ -56,6 +56,8 @@ public: static FILE* openMandatoryFile(const std::string& filename, const char* mode); + static bool file_exists(const std::string& filepath); + /* Returns full path for a game resource */ static inline std::string getResourcePath(const std::string& filepath) { return dataDir + '/' + filepath; } -- 2.39.5