NLS Engine  v0.1
The Next Logical Step in game engine design.
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines
main.cpp
Go to the documentation of this file.
00001 
00006 // *HACK We will disable the warning about no standard extension for scoped enums, as this is valid c++11
00007 // #pragma warning (disable : 4482) // Done through project property page
00008 
00009 #include <cassert>
00010 #include <set>
00011 
00012 #include <boost/thread.hpp>
00013 #include <boost/foreach.hpp>
00014 #include <boost/filesystem.hpp>
00015 
00016 #include <EngineConfig.h>
00017 
00018 #ifdef _WIN32
00019 #include "../windows/win32.h"
00020 #endif
00021 
00022 #include "../sharedbase/OSInterface.h"
00023 #include "../sharedbase/EntityList.h"
00024 #include "../sharedbase/EventLogger.h"
00025 #include "../sharedbase/PropertyMap.h"
00026 #include "../sharedbase/MessageRouter.h"
00027 #include "../sharedbase/Envelope.h"
00028 #include "ModuleManager.h"
00029 #include "ScriptModule.h"
00030 
00034 #define TRY_CONFIG_PARSE(dest, type, index) do {try {dest = boost::any_cast<type>(config->GetData(index));} catch (boost::bad_any_cast&) {operating_system->ShowWarning(NLS_I18N::CONFIG_PARSE_VALUE_ERROR + key); valid = false; continue;} catch (std::out_of_range&) {operating_system->ShowWarning(NLS_I18N::CONFIG_PARSE_VALUE_MISSING_ERROR + key); valid = false; continue;}} while(0)
00035 
00036 struct RequireFlags {
00037   RequireFlags(bool required) :
00038     required(required),
00039     multiple_allowed(false),
00040     found(false)
00041     {
00042   }
00043   
00044   RequireFlags(bool required, bool multiple_allowed) :
00045     required(required),
00046     multiple_allowed(multiple_allowed),
00047     found(false)
00048     {
00049   }
00050   
00051   bool required;
00052   bool multiple_allowed;
00053   bool found;
00054 };
00055 
00056 typedef std::map<std::string, RequireFlags> KeyRequiredMap;
00057 typedef KeyRequiredMap::value_type KeyRequired;
00058 
00059 int main() {
00060   std::string window_title, log_file_name = NLS_ENGINE_DEFAULT_LOG_FILE, main_script_path, main_script_function, userspace_game_path;
00061   unsigned int window_width, window_height;
00062   
00063   EventLogger* elog = EventLogger::GetEventLogger();
00064   EventLogger::module = "Main";
00065 
00066   // Create all of our global data managers
00067   PropertyMap* gprops = new PropertyMap();
00068   MessageRouter* msgrtr = new MessageRouter();
00069   EntityList emgr;
00070 
00071   gprops->SetProperty("UserGameFolder", "..");
00072 
00073   // Load the OS interface
00074 #ifdef _WIN32
00075   win32::Make(gprops, msgrtr);
00076 #endif
00077   assert(OSInterface::HasOS());
00078   OSInterfaceSPTR operating_system(OSInterface::GetOS());
00079   
00080   
00081   { // Load the system configuration from disk
00082   // *TODO: either make a serializable property tree (and replace property map's usage with it too) or make propertymap serializable.  Either way, use such here instead of the envelope.
00083     EnvelopeSPTR config(new Envelope());
00084     if (!LoadFromDisk(config, NLS_ENGINE_CONFIG_PATH)) {
00085 #ifndef _DEBUG
00086       operating_system->ShowError(NLS_I18N::CONFIG_LOAD_ERROR);
00087       
00088       return 1;
00089 #else
00090       operating_system->ShowInfo("Config data not found, creating from hard-coded values.");
00091       
00092       config->AddData(std::string("WindowTitle"));
00093       config->AddData(std::string("Game"));
00094       config->AddData(std::string("WindowSize"));
00095       config->AddData(800u);
00096       config->AddData(600u);
00097       config->AddData(std::string("LogFile"));
00098       config->AddData(std::string("Game.log"));
00099       config->AddData(std::string("MainScript"));
00100       config->AddData(std::string("/scripts/main.as"));
00101       config->AddData(std::string("void main(void)"));
00102       config->AddData(std::string("UserGameFolder"));
00103       config->AddData(std::string("NLS Game"));
00104       
00105       SaveToDisk(config, NLS_ENGINE_CONFIG_PATH);
00106 #endif
00107     }
00108     
00109     // Collect the valid keys
00110     KeyRequiredMap keys;
00111     keys.insert(KeyRequired("WindowTitle", RequireFlags(true)));
00112     keys.insert(KeyRequired("WindowSize", RequireFlags(true)));
00113     keys.insert(KeyRequired("UserGameFolder", RequireFlags(false)));
00114     keys.insert(KeyRequired("MainScript", RequireFlags(true)));
00115     keys.insert(KeyRequired("LogFile", RequireFlags(false)));
00116     
00117     // Parse and lex the loaded config file
00118     bool valid = true;
00119     std::string key;
00120     for (unsigned int index = 0; valid && index < config->GetCount(); ++index) {
00121       try {
00122         key = boost::any_cast<std::string>(config->GetData(index));
00123       }
00124       catch (boost::bad_any_cast&) {
00125         operating_system->ShowWarning(NLS_I18N::CONFIG_PARSE_KEY_ERROR);
00126         valid = false;
00127         continue; // Escape the loop so as to protect the following code from the error case.
00128       }
00129       
00130       KeyRequiredMap::iterator iter = keys.find(key);
00131       if (iter != keys.end()) {
00132         if (!iter->second.found || iter->second.multiple_allowed) {
00133           iter->second.found = true;
00134           
00135           if (key == "WindowTitle") {
00136             TRY_CONFIG_PARSE(window_title, std::string, ++index);
00137             
00138             LOG(LOG_PRIORITY::FLOW, "Config: Window title read as: " + window_title);
00139           }
00140           else if (key == "WindowSize") {
00141             TRY_CONFIG_PARSE(window_width, unsigned int, ++index);
00142             TRY_CONFIG_PARSE(window_height, unsigned int, ++index);
00143             
00144             LOG(LOG_PRIORITY::FLOW, "Config: Window size read as: <" + boost::lexical_cast<std::string>(window_width) + ", " + boost::lexical_cast<std::string>(window_height) + ">");
00145           }
00146           else if (key == "LogFile") {
00147             TRY_CONFIG_PARSE(log_file_name, std::string, ++index);
00148             
00149             LOG(LOG_PRIORITY::FLOW, "Config: Log file name read as: " + log_file_name);
00150           }
00151           else if (key == "MainScript") {
00152             TRY_CONFIG_PARSE(main_script_path, std::string, ++index);
00153             TRY_CONFIG_PARSE(main_script_function, std::string, ++index);
00154             
00155             LOG(LOG_PRIORITY::FLOW, "Config: Script file name read as: '" + main_script_path + "' with main function '" + main_script_function + "'");
00156           }
00157           else if (key == "UserGameFolder") {
00158             TRY_CONFIG_PARSE(userspace_game_path, std::string, ++index);
00159             
00160             userspace_game_path = operating_system->GetPath(DIRS::USER) + "/" + userspace_game_path;
00161             
00162             bool dir_exists = false;
00163             try {
00164               dir_exists = boost::filesystem::exists(userspace_game_path) && boost::filesystem::is_directory(userspace_game_path);
00165             }
00166             catch (boost::filesystem::filesystem_error& err) {
00167               operating_system->ShowError(NLS_I18N::FILESYSTEM_PER_USER_FOLDER_ACCESS_ERROR + err.what(), NLS_I18N::FILESYSTEM_ERROR_TITLE);
00168               valid = false;
00169             }
00170             
00171             if (valid) {
00172               if (dir_exists) {
00173                 // Attempt to modify it to see if it is writeable.
00174                 std::ofstream out_stream;
00175                 out_stream.open(userspace_game_path + "/FfSDFadf.txt", std::ios_base::out | std::ios_base::app);
00176                 if (!out_stream.bad()) {
00177                   out_stream << "test";
00178                   out_stream.close();
00179                   boost::filesystem::remove(userspace_game_path + "/FfSDFadf.txt");
00180                 }
00181                 else {
00182                   operating_system->ShowError(NLS_I18N::FILESYSTEM_PER_USER_FOLDER_WRITE_ERROR, NLS_I18N::FILESYSTEM_ERROR_TITLE);
00183                   valid = false;
00184                 }
00185               }
00186               else {
00187                 // Create it
00188                 LOG(LOG_PRIORITY::FLOW, "Creating per-user game configuration folder: " + userspace_game_path);
00189                 
00190                 try {
00191                   boost::filesystem::create_directories(userspace_game_path);
00192                 }
00193                 catch (boost::filesystem::filesystem_error& err) {
00194                   operating_system->ShowError(NLS_I18N::FILESYSTEM_PER_USER_FOLDER_CREATE_ERROR + err.what(), NLS_I18N::FILESYSTEM_ERROR_TITLE);
00195                   valid = false;
00196                 }
00197               }
00198               
00199               if (valid) {
00200                 gprops->SetProperty("UserGameFolder", userspace_game_path);
00201                 LOG(LOG_PRIORITY::FLOW, "Config: Per-user game folder file path read as: " + userspace_game_path);
00202               }
00203             }
00204           }
00205           else {
00206             assert(false /* Missing an if-statement! */);
00207           }
00208         }
00209         else {
00210           operating_system->ShowWarning(NLS_I18N::CONFIG_LEX_DUP_KEY_ERROR + key);
00211           valid = false;
00212         }
00213       }
00214       else {
00215         operating_system->ShowWarning(NLS_I18N::CONFIG_LEX_KEY_ERROR + key);
00216         valid = false;
00217       }
00218     }
00219     
00220     // Verify that all required keys were found
00221     BOOST_FOREACH(const KeyRequired& pair, keys) {
00222       if (pair.second.required && !pair.second.found) {
00223         operating_system->ShowWarning(NLS_I18N::CONFIG_KEY_MISSING_ERROR + pair.first);
00224         valid = false;
00225       }
00226     }
00227     
00228     if (!valid) {
00229       return 1;
00230     }
00231   }
00232 
00233   { // Set up the message logger with the file
00234     elog->SetLogFile(userspace_game_path + "/" + log_file_name); // *NOTE: After this point it is safe to log to disk without notifying the user of error conditions.
00235     LOG(LOG_PRIORITY::FLOW, "Log file created!");
00236   }
00237 
00238   // Start the message thread
00239   boost::thread msgThread(&MessageRouter::Route, msgrtr);
00240   
00241   { // Create our main window and store the handle the global properties
00242     HWND hwnd = boost::any_cast<HWND>(operating_system->CreateGUIWindow(window_width, window_height, window_title, WINDOW_INNER_SIZE));
00243     gprops->SetProperty("hwnd", hwnd);
00244   }
00245   
00246   // Start the module manager and the game's main script
00247   ModuleManager modmgr(gprops, msgrtr, &emgr);
00248   ScriptModule scripting(gprops, msgrtr, &emgr);
00249   bool script_ready = false;
00250   try {
00251     scripting.ExecuteFile(NLS_ENGINE_DATA_PATH + main_script_path, main_script_function);
00252     script_ready = true;
00253   }
00254   catch (ScriptException::TYPE&) {}
00255 
00256   if (script_ready) {
00258   //{
00259   //  EnvelopeSPTR e(new Envelope);
00260   //  e->msgid = CORE_MESSAGE::STARTUP;
00261   //  msgrtr->SendSP(e, false);
00262   //}
00263 
00264     // Timing variables used in the update function for the main loop.
00265     operating_system->SetupTimer();
00266     double elapsed = 0.0f;
00267 
00268     while (1) {
00269       operating_system->RouteMessages();
00270 
00271       elapsed = operating_system->GetElapsedTime();
00272 
00273       // Calls update for each core.
00274       if (msgThread.timed_join( boost::posix_time::time_duration(0,0,0,0)) == true) {
00275         break;
00276       }
00277 
00278       scripting.Update(elapsed);
00279       modmgr.Update(elapsed);
00280     }
00281 
00282     // Send the shutdown message to all the modules
00283     {
00284       EnvelopeSPTR e(new Envelope);
00285       e->msgid = CORE_MESSAGE::SHUTDOWN;
00286       msgrtr->SendSP(e, false);
00287     }
00288   }
00289   
00290   delete msgrtr; msgrtr = nullptr;
00291   delete gprops; gprops = nullptr;
00292   modmgr.Shutdown();
00293 
00294   return 0;
00295 }