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
# By default, all the settings are commented and not functional.
# Uncomment settings by removing the preceding #.
-#dedicated_server =
-
# Client side stuff
#wanted_fps = 30
{}
};
+class CommandLineError : public BaseException
+{
+public:
+ CommandLineError(const char *s):
+ BaseException(s)
+ {}
+};
+
/*
Some "old-style" interrupts:
*/
// Sets default settings\r
void set_default_settings()\r
{\r
- g_settings.set("dedicated_server", "");\r
-\r
// Client stuff\r
- g_settings.set("wanted_fps", "30");\r
- g_settings.set("fps_max", "60");\r
- g_settings.set("viewing_range_nodes_max", "300");\r
- g_settings.set("viewing_range_nodes_min", "35");\r
- g_settings.set("screenW", "");\r
- g_settings.set("screenH", "");\r
- g_settings.set("host_game", "");\r
- g_settings.set("port", "");\r
- g_settings.set("address", "");\r
- g_settings.set("name", "");\r
- g_settings.set("random_input", "false");\r
- g_settings.set("client_delete_unused_sectors_timeout", "1200");\r
- g_settings.set("max_block_send_distance", "8");\r
- g_settings.set("max_block_generate_distance", "6");\r
+ g_settings.setDefault("wanted_fps", "30");\r
+ g_settings.setDefault("fps_max", "60");\r
+ g_settings.setDefault("viewing_range_nodes_max", "300");\r
+ g_settings.setDefault("viewing_range_nodes_min", "35");\r
+ g_settings.setDefault("screenW", "");\r
+ g_settings.setDefault("screenH", "");\r
+ g_settings.setDefault("host_game", "");\r
+ g_settings.setDefault("port", "");\r
+ g_settings.setDefault("address", "");\r
+ g_settings.setDefault("name", "");\r
+ g_settings.setDefault("random_input", "false");\r
+ g_settings.setDefault("client_delete_unused_sectors_timeout", "1200");\r
+ g_settings.setDefault("max_block_send_distance", "8");\r
+ g_settings.setDefault("max_block_generate_distance", "6");\r
\r
// Server stuff\r
- g_settings.set("creative_mode", "false");\r
- g_settings.set("heightmap_blocksize", "32");\r
- g_settings.set("height_randmax", "constant 50.0");\r
- g_settings.set("height_randfactor", "constant 0.6");\r
- g_settings.set("height_base", "linear 0 0 0");\r
- g_settings.set("plants_amount", "1.0");\r
- g_settings.set("ravines_amount", "1.0");\r
- g_settings.set("objectdata_interval", "0.2");\r
- g_settings.set("active_object_range", "2");\r
- g_settings.set("max_simultaneous_block_sends_per_client", "1");\r
- g_settings.set("max_simultaneous_block_sends_server_total", "4");\r
- g_settings.set("disable_water_climb", "true");\r
- g_settings.set("endless_water", "true");\r
+ g_settings.setDefault("creative_mode", "false");\r
+ g_settings.setDefault("heightmap_blocksize", "32");\r
+ g_settings.setDefault("height_randmax", "constant 50.0");\r
+ g_settings.setDefault("height_randfactor", "constant 0.6");\r
+ g_settings.setDefault("height_base", "linear 0 0 0");\r
+ g_settings.setDefault("plants_amount", "1.0");\r
+ g_settings.setDefault("ravines_amount", "1.0");\r
+ g_settings.setDefault("objectdata_interval", "0.2");\r
+ g_settings.setDefault("active_object_range", "2");\r
+ g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");\r
+ g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");\r
+ g_settings.setDefault("disable_water_climb", "true");\r
+ g_settings.setDefault("endless_water", "true");\r
}\r
\r
/*\r
try\r
{\r
\r
+ /*\r
+ Parse command line\r
+ TODO\r
+ */\r
+ \r
+ core::map<std::string, ValueSpec> allowed_options;\r
+ allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG));\r
+ allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG,\r
+ "Run server directly"));\r
+ allowed_options.insert("config", ValueSpec(VALUETYPE_STRING,\r
+ "Load configuration from specified file"));\r
+ allowed_options.insert("port", ValueSpec(VALUETYPE_STRING));\r
+\r
+ Settings cmd_args;\r
+ \r
+ bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options);\r
+\r
+ if(ret == false || cmd_args.getFlag("help"))\r
+ {\r
+ dstream<<"Allowed options:"<<std::endl;\r
+ for(core::map<std::string, ValueSpec>::Iterator\r
+ i = allowed_options.getIterator();\r
+ i.atEnd() == false; i++)\r
+ {\r
+ dstream<<" --"<<i.getNode()->getKey();\r
+ if(i.getNode()->getValue().type == VALUETYPE_FLAG)\r
+ {\r
+ }\r
+ else\r
+ {\r
+ dstream<<" <value>";\r
+ }\r
+ dstream<<std::endl;\r
+\r
+ if(i.getNode()->getValue().help != NULL)\r
+ {\r
+ dstream<<" "<<i.getNode()->getValue().help\r
+ <<std::endl;\r
+ }\r
+ }\r
+\r
+ return cmd_args.getFlag("help") ? 0 : 1;\r
+ }\r
+\r
+\r
/*\r
Basic initialization\r
*/\r
Initialization\r
*/\r
\r
- // Read config file\r
+ /*\r
+ Read config file\r
+ */\r
\r
- if(argc >= 2)\r
+ // Path of configuration file in use\r
+ std::string configpath = "";\r
+ \r
+ if(cmd_args.exists("config"))\r
{\r
- g_settings.readConfigFile(argv[1]);\r
+ bool r = g_settings.readConfigFile(cmd_args.get("config").c_str());\r
+ if(r == false)\r
+ {\r
+ dstream<<"Could not read configuration from \""\r
+ <<cmd_args.get("config")<<"\""<<std::endl;\r
+ return 1;\r
+ }\r
+ configpath = cmd_args.get("config");\r
}\r
else\r
{\r
{\r
bool r = g_settings.readConfigFile(filenames[i]);\r
if(r)\r
+ {\r
+ configpath = filenames[i];\r
break;\r
+ }\r
}\r
}\r
\r
std::cout<<std::endl;\r
char templine[100];\r
\r
- // Dedicated?\r
- bool dedicated = g_settings.getBoolAsk\r
- ("dedicated_server", "Dedicated server?", false);\r
- std::cout<<"dedicated = "<<dedicated<<std::endl;\r
- \r
// Port?\r
- u16 port = g_settings.getU16Ask("port", "Port", 30000);\r
- std::cout<<"-> "<<port<<std::endl;\r
+ u16 port = 30000;\r
+ if(cmd_args.exists("port"))\r
+ {\r
+ port = cmd_args.getU16("port");\r
+ }\r
+ else\r
+ {\r
+ port = g_settings.getU16Ask("port", "Port", 30000);\r
+ std::cout<<"-> "<<port<<std::endl;\r
+ }\r
\r
- if(dedicated)\r
+ if(cmd_args.getFlag("server"))\r
{\r
DSTACK("Dedicated server branch");\r
\r
\r
{\r
TimeTaker timer("beginScene", device);\r
- driver->beginScene(true, true, bgcolor);\r
+ //driver->beginScene(true, true, bgcolor);\r
+ driver->beginScene(false, true, bgcolor);\r
beginscenetime = timer.stop(true);\r
}\r
\r
In the end, delete the Irrlicht device.\r
*/\r
device->drop();\r
+ \r
+ /*\r
+ Update configuration file\r
+ */\r
+ if(configpath != "")\r
+ {\r
+ g_settings.updateConfigFile(configpath.c_str());\r
+ }\r
\r
} //try\r
catch(con::PeerNotFoundException &e)\r
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)
//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)
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<<"...";
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<<"...";
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:
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();
}
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)
{
private:
core::map<std::string, std::string> m_settings;
+ core::map<std::string, std::string> m_defaults;
};
/*