From 385dd9917fd4ced23a704f0566779d58f5a6b727 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 14 Dec 2010 15:16:49 +0200 Subject: settings manager: better default setting handling and updating config file and command line parsing --- src/exceptions.h | 8 ++ src/main.cpp | 152 +++++++++++++++++++++++--------- src/socket.cpp | 4 +- src/test.cpp | 8 +- src/utility.h | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 380 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/exceptions.h b/src/exceptions.h index 0f95bd07a..95b9eea97 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -124,6 +124,14 @@ public: {} }; +class CommandLineError : public BaseException +{ +public: + CommandLineError(const char *s): + BaseException(s) + {} +}; + /* Some "old-style" interrupts: */ diff --git a/src/main.cpp b/src/main.cpp index 7814da999..b4e2d478c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -306,38 +306,36 @@ Settings g_settings; // Sets default settings void set_default_settings() { - g_settings.set("dedicated_server", ""); - // Client stuff - g_settings.set("wanted_fps", "30"); - g_settings.set("fps_max", "60"); - g_settings.set("viewing_range_nodes_max", "300"); - g_settings.set("viewing_range_nodes_min", "35"); - g_settings.set("screenW", ""); - g_settings.set("screenH", ""); - g_settings.set("host_game", ""); - g_settings.set("port", ""); - g_settings.set("address", ""); - g_settings.set("name", ""); - g_settings.set("random_input", "false"); - g_settings.set("client_delete_unused_sectors_timeout", "1200"); - g_settings.set("max_block_send_distance", "8"); - g_settings.set("max_block_generate_distance", "6"); + g_settings.setDefault("wanted_fps", "30"); + g_settings.setDefault("fps_max", "60"); + g_settings.setDefault("viewing_range_nodes_max", "300"); + g_settings.setDefault("viewing_range_nodes_min", "35"); + g_settings.setDefault("screenW", ""); + g_settings.setDefault("screenH", ""); + g_settings.setDefault("host_game", ""); + g_settings.setDefault("port", ""); + g_settings.setDefault("address", ""); + g_settings.setDefault("name", ""); + g_settings.setDefault("random_input", "false"); + g_settings.setDefault("client_delete_unused_sectors_timeout", "1200"); + g_settings.setDefault("max_block_send_distance", "8"); + g_settings.setDefault("max_block_generate_distance", "6"); // Server stuff - g_settings.set("creative_mode", "false"); - g_settings.set("heightmap_blocksize", "32"); - g_settings.set("height_randmax", "constant 50.0"); - g_settings.set("height_randfactor", "constant 0.6"); - g_settings.set("height_base", "linear 0 0 0"); - g_settings.set("plants_amount", "1.0"); - g_settings.set("ravines_amount", "1.0"); - g_settings.set("objectdata_interval", "0.2"); - g_settings.set("active_object_range", "2"); - g_settings.set("max_simultaneous_block_sends_per_client", "1"); - g_settings.set("max_simultaneous_block_sends_server_total", "4"); - g_settings.set("disable_water_climb", "true"); - g_settings.set("endless_water", "true"); + g_settings.setDefault("creative_mode", "false"); + g_settings.setDefault("heightmap_blocksize", "32"); + g_settings.setDefault("height_randmax", "constant 50.0"); + g_settings.setDefault("height_randfactor", "constant 0.6"); + g_settings.setDefault("height_base", "linear 0 0 0"); + g_settings.setDefault("plants_amount", "1.0"); + g_settings.setDefault("ravines_amount", "1.0"); + g_settings.setDefault("objectdata_interval", "0.2"); + g_settings.setDefault("active_object_range", "2"); + g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); + g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); + g_settings.setDefault("disable_water_climb", "true"); + g_settings.setDefault("endless_water", "true"); } /* @@ -962,6 +960,51 @@ int main(int argc, char *argv[]) try { + /* + Parse command line + TODO + */ + + core::map allowed_options; + allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG)); + allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, + "Run server directly")); + allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, + "Load configuration from specified file")); + allowed_options.insert("port", ValueSpec(VALUETYPE_STRING)); + + Settings cmd_args; + + bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); + + if(ret == false || cmd_args.getFlag("help")) + { + dstream<<"Allowed options:"<::Iterator + i = allowed_options.getIterator(); + i.atEnd() == false; i++) + { + dstream<<" --"<getKey(); + if(i.getNode()->getValue().type == VALUETYPE_FLAG) + { + } + else + { + dstream<<" "; + } + dstream<getValue().help != NULL) + { + dstream<<" "<getValue().help + <= 2) + // Path of configuration file in use + std::string configpath = ""; + + if(cmd_args.exists("config")) { - g_settings.readConfigFile(argv[1]); + bool r = g_settings.readConfigFile(cmd_args.get("config").c_str()); + if(r == false) + { + dstream<<"Could not read configuration from \"" + < "< "<beginScene(true, true, bgcolor); + //driver->beginScene(true, true, bgcolor); + driver->beginScene(false, true, bgcolor); beginscenetime = timer.stop(true); } @@ -2306,6 +2368,14 @@ int main(int argc, char *argv[]) In the end, delete the Irrlicht device. */ device->drop(); + + /* + Update configuration file + */ + if(configpath != "") + { + g_settings.updateConfigFile(configpath.c_str()); + } } //try catch(con::PeerNotFoundException &e) diff --git a/src/socket.cpp b/src/socket.cpp index 9ff30e10f..f4b8f4429 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -205,7 +205,7 @@ void UDPSocket::Send(const Address & destination, const void * data, int size) destination.print(); dstream<<", size="<20) @@ -267,7 +267,7 @@ int UDPSocket::Receive(Address & sender, void * data, int size) //dstream<<", received="<20) diff --git a/src/test.cpp b/src/test.cpp index ce7540e8d..313fe50db 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1070,8 +1070,8 @@ struct TestConnection dstream<<"Sending data (size="<<1100<<"):"; for(int i=0; i<1100 && i<20; i++){ - if(i%2==0) printf(" "); - printf("%.2X", ((int)((const char*)*data1)[i])&0xff); + if(i%2==0) DEBUGPRINT(" "); + DEBUGPRINT("%.2X", ((int)((const char*)*data1)[i])&0xff); } if(1100>20) dstream<<"..."; @@ -1091,8 +1091,8 @@ struct TestConnection dstream<<"Received data (size="<20) dstream<<"..."; diff --git a/src/utility.h b/src/utility.h index afcb4e414..f7e726f87 100644 --- a/src/utility.h +++ b/src/utility.h @@ -668,6 +668,23 @@ inline s32 stoi(std::string s) Config stuff */ +enum ValueType +{ + VALUETYPE_STRING, + VALUETYPE_FLAG // Doesn't take any arguments +}; + +struct ValueSpec +{ + ValueSpec(ValueType a_type, const char *a_help=NULL) + { + type = a_type; + help = a_help; + } + ValueType type; + const char *help; +}; + class Settings { public: @@ -710,36 +727,255 @@ public: return true; } - // Returns true on success + /* + Read configuration file + + Returns true on success + */ bool readConfigFile(const char *filename) { std::ifstream is(filename); if(is.good() == false) { - dstream<<"Error opening configuration file: " - < &dst, + core::map &updated) + { + if(is.eof()) + return false; + + // NOTE: This function will be expanded to allow multi-line settings + std::string line; + std::getline(is, line); + + std::string trimmedline = trim(line); + + std::string line_end = ""; + if(is.eof() == false) + line_end = "\n"; + + // Ignore comments + if(trimmedline[0] == '#') + { + dst.push_back(line+line_end); + return true; + } + + Strfnd sf(trim(line)); + + std::string name = sf.next("="); + name = trim(name); + + if(name == "") + { + dst.push_back(line+line_end); + return true; + } + + std::string value = sf.next("\n"); + value = trim(value); + + if(m_settings.find(name)) + { + std::string newvalue = m_settings[name]; + + if(newvalue != value) + { + dstream<<"Changing value of \""< \""< objects; + core::map updated; + + // Read and modify stuff + { + std::ifstream is(filename); + if(is.good() == false) + { + dstream<<"Error opening configuration file" + " for reading: \"" + <::Iterator + i = objects.begin(); + i != objects.end(); i++) + { + os<<(*i); + } + + /* + Write stuff that was not already in the file + */ + for(core::map::Iterator + i = m_settings.getIterator(); + i.atEnd() == false; i++) + { + if(updated.find(i.getNode()->getKey())) + continue; + std::string name = i.getNode()->getKey(); + std::string value = i.getNode()->getValue(); + dstream<<"Adding \""< &allowed_options) + { + int i=1; + for(;;) + { + if(i >= argc) + break; + std::string argname = argv[i]; + if(argname.substr(0, 2) != "--") + { + dstream<<"Invalid command-line parameter \"" + < expected."<::Node *n; + n = allowed_options.find(name); + if(n == NULL) + { + dstream<<"Unknown command-line parameter \"" + <getValue().type; + + std::string value = ""; + + if(type == VALUETYPE_FLAG) + { + value = "true"; + } + else + { + if(i >= argc) + { + dstream<<"Invalid command-line parameter \"" + <::Node *n; n = m_settings.find(name); if(n == NULL) - throw SettingNotFoundException("Setting not found"); + { + n = m_defaults.find(name); + if(n == NULL) + { + throw SettingNotFoundException("Setting not found"); + } + } return n->getValue(); } @@ -749,6 +985,18 @@ public: return is_yes(get(name)); } + bool getFlag(std::string name) + { + try + { + return getBool(name); + } + catch(SettingNotFoundException &e) + { + return false; + } + } + // Asks if empty bool getBoolAsk(std::string name, std::string question, bool def) { @@ -809,6 +1057,7 @@ public: private: core::map m_settings; + core::map m_defaults; }; /* -- cgit v1.2.3