From d01ab3755462b75cc4dd06dc6023a5f750a1b9f0 Mon Sep 17 00:00:00 2001 From: "Martin Felis (schakeline)" Date: Fri, 3 Dec 2010 00:15:26 +0100 Subject: [PATCH] introduced game data dir and user data dir - the game data dir contains system wide data such as levels, sounds, etc. - user data dir stores higscore, configurations --- CMakeLists.txt | 2 +- asteroids/Controller.cc | 2 +- asteroids/Model.cc | 20 ++++----- asteroids/ShipEntity.cc | 2 +- asteroids/View.cc | 10 ++--- asteroids/main.cc | 98 ++++++++++++++++++++++++++++++++++++++++- engine/Commands.cc | 42 +++++++++++++++++- engine/Engine.cc | 18 ++++++++ engine/Engine.h | 34 ++++++++++++++ engine/Sprite.cc | 18 ++++---- engine/Sprite.h | 2 +- engine/ViewBase.cc | 2 +- 12 files changed, 217 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68b918e..2031917 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -PROJECT ( Asteroids CXX ) +PROJECT ( Asteroids C CXX ) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) diff --git a/asteroids/Controller.cc b/asteroids/Controller.cc index 8ce48f3..96429b0 100644 --- a/asteroids/Controller.cc +++ b/asteroids/Controller.cc @@ -33,7 +33,7 @@ bool Controller::OnReceiveEvent (const Engine::EventBasePtr &event) { } void Controller::ResetPlayerEntity () { - Engine::HaltSoundLoop("./data/sounds/thrust.wav"); + Engine::HaltSoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav")); Engine::EntityBase *player_entity = GetModel()->GetEntity(GetModel()->GetPlayerEntityId()); diff --git a/asteroids/Model.cc b/asteroids/Model.cc index 4864ad3..24f65b5 100644 --- a/asteroids/Model.cc +++ b/asteroids/Model.cc @@ -65,7 +65,7 @@ int Model::OnInit (int argc, char* argv[]) { mPlayerName = "Player"; - Engine::PlayMusic ("./data/sounds/intro_music.ogg"); + Engine::PlayMusic (Engine::GetResourceFullPath("/data/sounds/intro_music.ogg")); Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); @@ -76,10 +76,10 @@ int Model::OnInit (int argc, char* argv[]) { bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) { switch (event->mEventType) { case EventAccelerateStart: - Engine::PlaySoundLoop("./data/sounds/thrust.wav", -1); + Engine::PlaySoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav"), -1); break; case EventAccelerateStop: - Engine::HaltSoundLoop("./data/sounds/thrust.wav"); + Engine::HaltSoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav")); break; case EventShipExplode: OnShipExplode(); @@ -117,19 +117,19 @@ void Model::Process () { } unsigned int Model::InitLevelList () { - const char* level_dir_name = "./data/levels/"; - Engine::LogDebug ("Searching for levels in %s", level_dir_name); + std::string level_dir_name = Engine::GetResourceFullPath("/data/levels/"); + Engine::LogDebug ("Searching for levels in %s", level_dir_name.c_str()); mLevelList.clear(); boost::filesystem::path level_dir(level_dir_name); if (!boost::filesystem::exists(level_dir)) { - Engine::LogError ("Could not init level list: %s does not exist!"); + Engine::LogError ("Could not init level list: \todo %s does not exist!"); } if (!boost::filesystem::is_directory(level_dir)) { - Engine::LogError ("Could not init level list: %s is not a directory!"); + Engine::LogError ("Could not init level list: \todo %s is not a directory!"); } boost::filesystem::directory_iterator end_iter; @@ -151,7 +151,7 @@ unsigned int Model::InitLevelList () { void Model::LoadHighscoreList () { Engine::LogDebug ("Loading highscore file"); - boost::filesystem::path highscore_file("./highscore.dat"); + boost::filesystem::path highscore_file(Engine::GetUserDirFullPath("/highscore.dat")); // if the file does not exist, we create it and write standard values into // it. @@ -185,7 +185,7 @@ void Model::LoadHighscoreList () { } void Model::SaveHighscoreList () { - std::ofstream highscore_file ("./highscore.dat"); + std::ofstream highscore_file (Engine::GetUserDirFullPath("/highscore.dat").c_str()); std::list::iterator iter = mHighscoreList.begin(); @@ -425,7 +425,7 @@ void Model::OnKillEntity (const Engine::EntityBase *entity) { GameEntityType entity_type = (GameEntityType) entity->mType; if (entity_type == GameEntityTypeAsteroid) { - Engine::PlaySound("./data/sounds/rock_destroyed.wav"); + Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav")); unsigned int i; const AsteroidEntity *asteroid = static_cast(entity); diff --git a/asteroids/ShipEntity.cc b/asteroids/ShipEntity.cc index 26f31d1..9725bc4 100644 --- a/asteroids/ShipEntity.cc +++ b/asteroids/ShipEntity.cc @@ -117,7 +117,7 @@ void ShipEntity::Attack () { rocket_physics->mVelocity = attack_dir.normalize(); rocket_physics->mVelocity *= ShipEntity::VarMaxSpeed.GetFloatValue() + 0.5; - Engine::PlaySound ("./data/sounds/laser.wav"); + Engine::PlaySound (Engine::GetResourceFullPath("/data/sounds/laser.wav")); } } diff --git a/asteroids/View.cc b/asteroids/View.cc index 09141a0..c6a21ed 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -56,16 +56,16 @@ int View::OnInit (int argc, char* argv[]) { mBackgroundStars.push_back (star); } - mGUIShipSprite.LoadFromPNG("./data/textures/ship.png"); + mGUIShipSprite.LoadFromPNG(Engine::GetResourceFullPath("/data/textures/ship.png")); mGUIShipSprite.SetScale (0.1); - mAsteroidSprite.LoadFromPNG ("./data/textures/asteroid.png"); - mShipSprite.LoadFromPNG ("./data/textures/ship.png"); + mAsteroidSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/asteroid.png")); + mShipSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship.png")); - mShipThrustSprite.LoadFromPNG ("./data/textures/ship_thrust.png"); + mShipThrustSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship_thrust.png")); mShipThrustSprite.SetAnimation (4, 8); - mShipPartsSprite.LoadFromPNG ("./data/textures/ship_parts.png"); + mShipPartsSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship_parts.png")); mShipPartsSprite.SetSubSpriteCount (10); Engine::RegisterListener (this, EventAccelerateStart); diff --git a/asteroids/main.cc b/asteroids/main.cc index 028bbd6..0c5cf87 100644 --- a/asteroids/main.cc +++ b/asteroids/main.cc @@ -9,12 +9,94 @@ #include "Physics.h" #include "EntityFactory.h" +#include + #ifdef WIN32 #include #endif using namespace std; +/* Returns a path where files such as logs and config files can be + * written to + */ +std::string create_user_path () { + std::string result_dir = "."; + std::string test_file_path = result_dir; + + // first we check in $HOME/.fysxasteroids + char* env_home_dir = getenv("HOME"); + result_dir = env_home_dir; + result_dir += "/.fysxasteroids"; + + boost::filesystem::path result_dir_path(result_dir); + if(!boost::filesystem::is_directory (result_dir_path)) { + if (!boost::filesystem::create_directory(result_dir_path)) { + cerr << "Warning: could not create user data directory " << result_dir<< endl; + result_dir = ""; + } + } + + test_file_path = result_dir; + test_file_path += "/game.log"; + ofstream test_file (test_file_path.c_str(), ios_base::app); + if (!test_file) { + test_file.close(); + cerr << "Warning: user data directory not writable! " << result_dir << endl; + result_dir = ""; + } else { + test_file.close(); + return result_dir; + } + + // then we check the local directory + result_dir = "."; + test_file_path = result_dir; + test_file_path += "/game.log"; + test_file.open (test_file_path.c_str(), ios_base::out); + if (test_file) { + test_file.close(); + return result_dir; + } else { + cerr << "Warning could not find suitable user data directory" << endl; + result_dir = ""; + } + test_file.close(); + + return result_dir; +} + +std::string find_game_data_dir () { + std::string result; + + std::vector paths; + paths.push_back("."); + paths.push_back("/usr/local/share/fysxasteroids"); + paths.push_back("/usr/share/fysxasteroids"); + + std::vector::iterator iter = paths.begin(); + for (iter; iter != paths.end(); iter++) { + std::string test_path = *iter; + + if (!boost::filesystem::is_directory(test_path + "/data/fonts")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/levels")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/sounds")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/textures")) + continue; + + break; + } + + if (iter != paths.end()) + return *iter; + + cerr << "Could not find game data" << endl; + return result; +} + int main (int argc, char* argv[]) { cout << "Game Start" << endl; @@ -32,7 +114,19 @@ int main (int argc, char* argv[]) { engine.SetView (new asteroids::View); SetLogPrintLevel (Engine::LogLevelMessage); - Engine::SetLogFilename ("game.log"); + + // we assume the user path to be local folder + std::string user_path = create_user_path(); + std::string log_file_path = user_path; + log_file_path += "/game.log"; + + cout << "User Data Dir = " << user_path << endl; + engine.SetUserDataPath (user_path); + Engine::SetLogFilename (log_file_path.c_str()); + + std::string game_data_path = find_game_data_dir(); + engine.SetGameDataPath (game_data_path); + cout << "Game Data Dir = " << game_data_path << endl; if (engine.Init (argc, argv) != 0) { cout << "Could not start engine!" << endl; @@ -60,7 +154,7 @@ int main (int argc, char* argv[]) { engine.MainLoop (); // save the configuration - std::ofstream config_file ("config.rc"); + std::ofstream config_file (engine.GetUserDirFullPath("/config.rc").c_str()); config_file << "set effects_volume " << Engine::GetEffectsVolume() << std::endl; config_file << "set music_volume " << Engine::GetMusicVolume() << std::endl; config_file.close(); diff --git a/engine/Commands.cc b/engine/Commands.cc index b2d2f09..ed4b720 100644 --- a/engine/Commands.cc +++ b/engine/Commands.cc @@ -1,4 +1,5 @@ #include "Commands.h" +#include namespace Engine { @@ -190,6 +191,40 @@ std::string CommandGetErrorString (){ return CommandsInstance->GetErrorString(); } +/** \brief Searches for possible candidates at reasonable places + * + * In that order: + * 1. current directory + * 2. user data directory + * 3. game data directory + * + * \returns full path to the file, otherwise only the filename (which will + * \returns then cause an error because the file cannot be opened) + */ +std::string find_exec_file_full_path (const std::string &exec_file) { + std::string full_path = exec_file; + + boost::filesystem::path exec_file_path (full_path); + if(boost::filesystem::is_regular_file(exec_file_path)) { + return full_path; + } + + full_path = GetUserDirFullPath(std::string("/") + exec_file); + exec_file_path = full_path; + if(boost::filesystem::is_regular_file(exec_file_path)) { + return full_path; + } + + full_path = GetResourceFullPath(std::string("/") + exec_file); + exec_file_path = full_path; + if(boost::filesystem::is_regular_file(exec_file_path)) { + return full_path; + } + + // otherwise just return the normal path which will fail anyway + return exec_file; +} + /* * Commands of the Command system */ @@ -202,13 +237,16 @@ bool Cmd_Exec (const std::vector args) { return false; } + std::string full_path = find_exec_file_full_path(args[0]); + LogDebug ("Trying to exec file %s", full_path.c_str()); + std::ifstream exec_file; - exec_file.open(args[0].c_str(), std::ios_base::in); + exec_file.open(full_path.c_str(), std::ios_base::in); if (!exec_file) { std::ostringstream error_msg; error_msg << "exec failed: could not open file '" - << args[0] << "'"; + << full_path.c_str() << "'"; CommandsInstance->SetErrorString(error_msg.str()); return false; } diff --git a/engine/Engine.cc b/engine/Engine.cc index a18f812..dc60a1a 100644 --- a/engine/Engine.cc +++ b/engine/Engine.cc @@ -363,5 +363,23 @@ ControllerBase* EngineGetController () { return EngineInstance->GetController(); } +std::string GetResourceFullPath (const std::string &resource) { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetResourceFullPath(resource); +} + +std::string GetUserDirFullPath (const std::string &path) { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetUserDirFullPath(path); +} + } diff --git a/engine/Engine.h b/engine/Engine.h index 15e3932..9d7da25 100644 --- a/engine/Engine.h +++ b/engine/Engine.h @@ -99,6 +99,29 @@ class Engine : public Module { void SetStatus (const EngineStatus new_status); EngineStatus GetStatus (); + void SetUserDataPath (const std::string &path) { + mUserDataPath = path; + }; + std::string GetUserDataPath () { return mUserDataPath; }; + + void SetGameDataPath (const std::string &path) { + mGameDataPath = path; + }; + std::string GetGameDataPath () { return mGameDataPath; }; + + /** \brief Returns the path to a resource by prepending the game data path to + * \brief it + */ + std::string GetResourceFullPath (const std::string &resource) { + return mGameDataPath + resource; + }; + + /** \brief Returns the path to a file by prepending the user data path to it + */ + std::string GetUserDirFullPath (const std::string &path) { + return mUserDataPath + path; + }; + private: // Engine must not be created with the standard constructor! // It must be ensured, that the correct EntityFactory is used! @@ -121,6 +144,9 @@ class Engine : public Module { Commands *mCommands; Variables *mVariables; + std::string mUserDataPath; + std::string mGameDataPath; + std::string mErrorString; EngineStatus mStatus; }; @@ -152,6 +178,14 @@ ViewBase* EngineGetView (); /** \brief Global access functions for the Controller */ ControllerBase* EngineGetController (); +/** \brief Returns the path to a resource by prepending the game data path to + * \brief it + */ +std::string GetResourceFullPath (const std::string &resource); + +/** \brief Returns the path to a file by prepending the user data path to it + */ +std::string GetUserDirFullPath (const std::string &path); } /* Include the globally visible declarations of the other modules */ diff --git a/engine/Sprite.cc b/engine/Sprite.cc index 3c30cd1..9fa50e7 100644 --- a/engine/Sprite.cc +++ b/engine/Sprite.cc @@ -13,16 +13,16 @@ namespace Engine { * Code is taken from http://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures on * Sunday, March 14 2010. */ -bool Sprite::LoadFromPNG (const char *filename) { - LogDebug ("Loading png from %s", filename); +bool Sprite::LoadFromPNG (const std::string &filename) { + LogDebug ("Loading png from %s", filename.c_str()); //header for testing if it is a png png_byte header[8]; //open file as binary - FILE *fp = fopen(filename, "rb"); + FILE *fp = fopen(filename.c_str(), "rb"); if (!fp) { - LogError ("Could not open file: %s", filename); + LogError ("Could not open file: %s", filename.c_str()); return false; } @@ -32,7 +32,7 @@ bool Sprite::LoadFromPNG (const char *filename) { //test if png int is_png = !png_sig_cmp(header, 0, 8); if (!is_png) { - LogError ("Error opening png file %s: file is not a png file!", filename); + LogError ("Error opening png file %s: file is not a png file!", filename.c_str()); fclose(fp); return false; } @@ -41,7 +41,7 @@ bool Sprite::LoadFromPNG (const char *filename) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { - LogError ("Error opening png file %s: unable to read png header", filename); + LogError ("Error opening png file %s: unable to read png header", filename.c_str()); fclose(fp); return (false); } @@ -49,7 +49,7 @@ bool Sprite::LoadFromPNG (const char *filename) { //create png info struct png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { - LogError ("Error opening png file %s: unable to read png header", filename); + LogError ("Error opening png file %s: unable to read png header", filename.c_str()); png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); fclose(fp); return (false); @@ -58,7 +58,7 @@ bool Sprite::LoadFromPNG (const char *filename) { //create png info struct png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { - LogError ("Error opening png file %s: unable to read png header", filename); + LogError ("Error opening png file %s: unable to read png header", filename.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); fclose(fp); return (false); @@ -66,7 +66,7 @@ bool Sprite::LoadFromPNG (const char *filename) { //png error stuff, not sure libpng man suggests this. if (setjmp(png_jmpbuf(png_ptr))) { - LogError ("Error opening png file %s: unable to read png header", filename); + LogError ("Error opening png file %s: unable to read png header", filename.c_str()); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return (false); diff --git a/engine/Sprite.h b/engine/Sprite.h index 411856a..8a2adfd 100644 --- a/engine/Sprite.h +++ b/engine/Sprite.h @@ -17,7 +17,7 @@ class Sprite { mSubSpriteCount = 1; } - bool LoadFromPNG (const char *filename); + bool LoadFromPNG (const std::string &filename); void DrawAt (float xpos, float ypos, float zpos); void DrawAt2D (float xpos, float ypos); unsigned int GetWidth() { return mWidth; }; diff --git a/engine/ViewBase.cc b/engine/ViewBase.cc index e820b4c..4065554 100644 --- a/engine/ViewBase.cc +++ b/engine/ViewBase.cc @@ -275,7 +275,7 @@ bool ViewBase::LoadFont (const std::string &font_spec_string) { parse_font_spec_string(font_spec_string, font_name, font_color, &font_size); - std::string font_path ("./data/fonts/"); + std::string font_path (GetResourceFullPath("/data/fonts/")); font_path += font_name; LogDebug ("Loading font %s color (%1.2f, %1.2f, %1.2f) size %f from %s", font_name.c_str(), font_color[0], font_color[1], font_color[2], font_size, font_path.c_str());