diff options
-rw-r--r-- | doc/README.txt | 3 | ||||
-rw-r--r-- | minetest.conf.example | 2 | ||||
-rw-r--r-- | src/exceptions.h | 8 | ||||
-rw-r--r-- | src/main.cpp | 152 | ||||
-rw-r--r-- | src/socket.cpp | 4 | ||||
-rw-r--r-- | src/test.cpp | 8 | ||||
-rw-r--r-- | src/utility.h | 261 |
7 files changed, 382 insertions, 56 deletions
diff --git a/doc/README.txt b/doc/README.txt index 0cb31da38..3467564bd 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -32,7 +32,8 @@ Controls: Configuration file: - An optional configuration file can be used. See minetest.conf.example. -- Path to file can be passed as a parameter to the executable. +- Path to file can be passed as a parameter to the executable: + --config <path-to-file> - If not given as a parameter, these are checked, in order: ../minetest.conf ../../minetest.conf diff --git a/minetest.conf.example b/minetest.conf.example index 21ce10721..aa38c8c7a 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -7,8 +7,6 @@ # By default, all the settings are commented and not functional. # Uncomment settings by removing the preceding #. -#dedicated_server = - # Client side stuff #wanted_fps = 30 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");
}
/*
@@ -963,6 +961,51 @@ int main(int argc, char *argv[]) {
/*
+ Parse command line
+ TODO
+ */
+
+ core::map<std::string, ValueSpec> 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:"<<std::endl;
+ for(core::map<std::string, ValueSpec>::Iterator
+ i = allowed_options.getIterator();
+ i.atEnd() == false; i++)
+ {
+ dstream<<" --"<<i.getNode()->getKey();
+ if(i.getNode()->getValue().type == VALUETYPE_FLAG)
+ {
+ }
+ else
+ {
+ dstream<<" <value>";
+ }
+ dstream<<std::endl;
+
+ if(i.getNode()->getValue().help != NULL)
+ {
+ dstream<<" "<<i.getNode()->getValue().help
+ <<std::endl;
+ }
+ }
+
+ return cmd_args.getFlag("help") ? 0 : 1;
+ }
+
+
+ /*
Basic initialization
*/
@@ -999,11 +1042,23 @@ int main(int argc, char *argv[]) Initialization
*/
- // Read config file
+ /*
+ Read config file
+ */
- if(argc >= 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 \""
+ <<cmd_args.get("config")<<"\""<<std::endl;
+ return 1;
+ }
+ configpath = cmd_args.get("config");
}
else
{
@@ -1017,7 +1072,10 @@ int main(int argc, char *argv[]) {
bool r = g_settings.readConfigFile(filenames[i]);
if(r)
+ {
+ configpath = filenames[i];
break;
+ }
}
}
@@ -1059,16 +1117,19 @@ int main(int argc, char *argv[]) std::cout<<std::endl;
char templine[100];
- // Dedicated?
- bool dedicated = g_settings.getBoolAsk
- ("dedicated_server", "Dedicated server?", false);
- std::cout<<"dedicated = "<<dedicated<<std::endl;
-
// Port?
- u16 port = g_settings.getU16Ask("port", "Port", 30000);
- std::cout<<"-> "<<port<<std::endl;
+ u16 port = 30000;
+ if(cmd_args.exists("port"))
+ {
+ port = cmd_args.getU16("port");
+ }
+ else
+ {
+ port = g_settings.getU16Ask("port", "Port", 30000);
+ std::cout<<"-> "<<port<<std::endl;
+ }
- if(dedicated)
+ if(cmd_args.getFlag("server"))
{
DSTACK("Dedicated server branch");
@@ -2208,7 +2269,8 @@ int main(int argc, char *argv[]) {
TimeTaker timer("beginScene", device);
- driver->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="<<size<<", data="; for(int i=0; i<size && i<20; i++){ - if(i%2==0) printf(" "); + if(i%2==0) DEBUGPRINT(" "); DEBUGPRINT("%.2X", ((int)((const char*)data)[i])&0xff); } if(size>20) @@ -267,7 +267,7 @@ int UDPSocket::Receive(Address & sender, void * data, int size) //dstream<<", received="<<received<<std::endl; dstream<<", size="<<received<<", data="; for(int i=0; i<received && i<20; i++){ - if(i%2==0) printf(" "); + if(i%2==0) DEBUGPRINT(" "); DEBUGPRINT("%.2X", ((int)((const char*)data)[i])&0xff); } if(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="<<size<<"):"; for(int i=0; i<size && i<20; i++){ - if(i%2==0) printf(" "); - printf("%.2X", ((int)((const char*)recvdata)[i])&0xff); + if(i%2==0) DEBUGPRINT(" "); + DEBUGPRINT("%.2X", ((int)((const char*)recvdata)[i])&0xff); } if(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: " - <<filename<<std::endl; + dstream<<"Error opening configuration file \"" + <<filename<<"\""<<std::endl; return false; } - dstream<<"Parsing configuration file: " - <<filename<<std::endl; + dstream<<"Parsing configuration file: \"" + <<filename<<"\""<<std::endl; while(parseConfigObject(is)); return true; } + /* + Reads a configuration object from stream (usually a single line) + and adds it to dst. + + Preserves comments and empty lines. + + Settings that were added to dst are also added to updated. + key of updated is setting name, value of updated is dummy. + + Returns false on EOF + */ + bool getUpdatedConfigObject(std::istream &is, + core::list<std::string> &dst, + core::map<std::string, bool> &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 \""<<name<<"\" = \"" + <<value<<"\" -> \""<<newvalue<<"\"" + <<std::endl; + } + + dst.push_back(name + " = " + newvalue + line_end); + + updated[name] = true; + } + + return true; + } + + /* + Updates configuration file + + Returns true on success + */ + bool updateConfigFile(const char *filename) + { + dstream<<"Updating configuration file: \"" + <<filename<<"\""<<std::endl; + + core::list<std::string> objects; + core::map<std::string, bool> updated; + + // Read and modify stuff + { + std::ifstream is(filename); + if(is.good() == false) + { + dstream<<"Error opening configuration file" + " for reading: \"" + <<filename<<"\""<<std::endl; + return false; + } + + while(getUpdatedConfigObject(is, objects, updated)); + } + + // Write stuff back + { + std::ofstream os(filename); + if(os.good() == false) + { + dstream<<"Error opening configuration file" + " for writing: \"" + <<filename<<"\""<<std::endl; + return false; + } + + /* + Write updated stuff + */ + for(core::list<std::string>::Iterator + i = objects.begin(); + i != objects.end(); i++) + { + os<<(*i); + } + + /* + Write stuff that was not already in the file + */ + for(core::map<std::string, std::string>::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 \""<<name<<"\" = \""<<value<<"\"" + <<std::endl; + os<<name<<" = "<<value<<"\n"; + } + } + + return true; + } + + /* + NOTE: Types of allowed_options are ignored + + returns true on success + */ + bool parseCommandLine(int argc, char *argv[], + core::map<std::string, ValueSpec> &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 \"" + <<argname<<"\": --<option> expected."<<std::endl; + return false; + } + i++; + + std::string name = argname.substr(2); + + core::map<std::string, ValueSpec>::Node *n; + n = allowed_options.find(name); + if(n == NULL) + { + dstream<<"Unknown command-line parameter \"" + <<argname<<"\""<<std::endl; + return false; + } + + ValueType type = n->getValue().type; + + std::string value = ""; + + if(type == VALUETYPE_FLAG) + { + value = "true"; + } + else + { + if(i >= argc) + { + dstream<<"Invalid command-line parameter \"" + <<name<<"\": missing value"<<std::endl; + return false; + } + value = argv[i]; + i++; + } + + + dstream<<"Valid command-line parameter: \"" + <<name<<"\" = \""<<value<<"\"" + <<std::endl; + set(name, value); + } + + return true; + } + void set(std::string name, std::string value) { m_settings[name] = value; } + void setDefault(std::string name, std::string value) + { + m_defaults[name] = value; + } + + bool exists(std::string name) + { + return (m_settings.find(name) || m_defaults.find(name)); + } + std::string get(std::string name) { core::map<std::string, std::string>::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<std::string, std::string> m_settings; + core::map<std::string, std::string> m_defaults; }; /* |