/* $Id: wopsettings.cpp,v 1.81.2.3 2006/01/24 10:08:10 chfreund Exp $ */ #include "global.hpp" #include "constants.hpp" #include "wopsettings.hpp" #include "spriteset.hpp" #include "string.hpp" #include #include #ifdef WIN32 #define NO_HOME_PATH_EXPANSION #endif #ifndef NO_HOME_PATH_EXPANSION #include #endif /**********************************************************/ // definition of some standard attributes // STD_ATTR : standard setting // - explicit assignment by "=" // - comma separation of parameters possible #define STD_ATTR (Setting::EXPLICIT_ASSIGNMENT | \ Setting::COMMA_SEPARATED) // STD_ATTR_MAND : standard setting, but mandatory #define STD_ATTR_MAND (STD_ATTR | Setting::MANDATORY) // STD_ATTR_CLO : standard settings, command line only #define STD_ATTR_CLO (STD_ATTR | Setting::COMMAND_LINE_ONLY) // STD_ATTR_CFO : standard settings, config file only #define STD_ATTR_CFO (STD_ATTR | Setting::CONFIG_FILE_ONLY) /********************************************************** * definition of settings and string maps **********************************************************/ // the default configuration file is defined as a macro, // since it is also used in the help string of the setting // commented out on 15.Jan 2005 (uwe) //// #if defined(__APPLE__) || defined(WIN32) //// #define DEFAULT_WOP_CONFIG_FILE "woprc.txt" //// #else //// #define DEFAULT_WOP_CONFIG_FILE ".woprc" //// #endif // Here the standard search paths for the config file are set, // INCLUDING their names. // This list also defines the hierarchy of the settings' usage. // First the command line is parsed and the specified settings are // accepted. Then one by one config file from the list defined in // m_configPaths is read, and the settings are AUGMENTED with the // ones in that file, NOT OVERWRITTEN. This alows for example the // definition of a standard system-wide configuration file and a // userspecific configuration file by setting: // // const char* WopSettings::m_configPaths[] = { // "~/.woprc", "/usr/local/config/sys_woprc", NULL }; // // The list must be terminated by a 0x0 pointer as last entry. // // So the settings are read in the following hierarchy: // // HIGHEST PRIORITY // -> command line options // -> options in configuration file, that has been // specified optionally in the commandline using // the setting "config=" // -> m_configPaths[0] // : // : // LOWEST PRIORITY // const char* WopSettings::m_configPaths[] = { #ifdef __APPLE__ "~/woprc.txt", #else "~/.woprc", // home directory "/etc/woprc", // system wide #endif 0x0 }; /**********************************************************/ // definition of the basic settings SettingDef WopSettings::m_basicSettings[] = { // the members are: // (1) ID string of the setting // (2) alternative ID string (optional, can also be set to NULL) // (3) parameter type of the setting (Setting::[INT,BOOL,FLOAT,STRING]) // (4) minimal number of parameters for the setting // (5) maximal number of parameters for the setting (-1 means unlimited) // (6) attributes for the setting (as default use STD_ATTR, for more // details read documentation of class SettingDef in settings.hpp // (7) if the new setting is a server setting, that should be sent to // the clients, add its IDstring to m_serverSettingsIDs // // try to limit line length of the help to maximal 60 characters |<------ 60 characters, including built in indent ------>| // mode -> SettingStringMap WopSettings::m_mode_StringMap[] SettingDef( "mode", "m", Setting::STRING, 1, 1, STD_ATTR_MAND, "program mode [server,client,replay]", 0 ), SettingDef( "gamemode","gm", Setting::STRING, 1, 1, STD_ATTR, "game mode (server only) [deathmatch,teamplay]", 0 ), SettingDef( "data", "d", Setting::STRING, 1, 1, STD_ATTR, "path of data directory\n " "default: \"data\", then /usr/local/games/wop/data", 0 ), SettingDef( "server", "s", Setting::STRING, 1, 1, STD_ATTR, "server address. default: localhost", 0 ), SettingDef( "width", "w", Setting::INT, 1, 1, STD_ATTR, "width of the game map: > 0 (server only)", 0 ), SettingDef( "height", "h", Setting::INT, 1, 1, STD_ATTR, "height of the game map: > 0 (server only)", 0 ), SettingDef( "theme", 0x0, Setting::STRING, 1, 1, STD_ATTR, "name of the map theme (effect on server only)\n " "possible theme names are specified in file "THEME_CONFIG"\n " "in the data directory (default: \"standard\").\n " "To get a list of the available themes simply specify a\n " "non-valid theme, e.g. theme=?", 0 ), // view -> SettingStringMap WopSettings::m_view_StringMap[] SettingDef( "view", "v", Setting::STRING, 1, 1, STD_ATTR, "view mode: [fullscreen,window], default: window", 0 ), SettingDef( "quiet", "q", Setting::BOOL , 0, 1, STD_ATTR, "no sound output: [yes,no], default: no", 0 ), SettingDef( "config", "c", Setting::STRING, 1, 1, STD_ATTR_CLO, "path of the configuration file", 0 ), SettingDef( "name", "n", Setting::STRING, 1, 1, STD_ATTR, "the name of the player", 0 ), SettingDef( "color", "c", Setting::INT, 3, 3, STD_ATTR, "the color of the player's avatar, specified in the\n " "three color components RED, GREEN, BLUE in this order.\n " "The RGB values must be in range [0,255]" ), SettingDef( "team", "t", Setting::INT, 1, 1, STD_ATTR, "the team number (0 <= team <= 19, only considered in \n " "team mode)", 0 ), SettingDef( "keyboard","kb", Setting::STRING, 1, 1, STD_ATTR, "Keyboard layout (de/us)" ), SettingDef( "nballs", "nb", Setting::INT, 1, 1, STD_ATTR, "number of balls in the game", 0 ), SettingDef( "ngoals", "ng", Setting::INT, 1, 1, STD_ATTR, "number of goals in the game", 0 ), SettingDef( "szprob", 0x0, Setting::REAL, 1, 1, STD_ATTR, "probability for the creation of SkwoermZones [0.0, 1.0]", 0 ), SettingDef( "debug", "db", Setting::BOOL, 0, 1, STD_ATTR, "debug mode: [yes,no], default: no", 0 ), SettingDef( "musicdir", 0x0, Setting::STRING, 1, 1, STD_ATTR, "path of a directory containing sound files of type\n " "*.wav, *.mp3, *.ogg, that are played randomly during\n " "the game.", 0 ), SettingDef( "bots", 0x0, Setting::STRING, 1,-1, STD_ATTR, "list of bot names" ), SettingDef( "replaylog",0x0, Setting::STRING, 1, 1, STD_ATTR, "name of replay log file. This setting also activates\n " "the replay logging (server only). To view the replay\n " "in the specified file choose \"mode=replay\"", 0 ), SettingDef( "keyhelp", 0x0, Setting::BOOL, 1, -1, STD_ATTR_CLO, "prints the long list of possible key configurations on\n " "the terminal.", 0 ), // settings for keyboard configuration SettingDef( "key_left", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "left key (configuration file only)", 10 ), SettingDef( "key_right", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "right key (configuration file only)", 10 ), SettingDef( "key_down", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "down key (configuration file only)", 10 ), SettingDef( "key_up", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "up key (configuration file only)", 10 ), SettingDef( "key_shoot", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "shoot key (configuration file only)", 10 ), SettingDef( "key_jump", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "jump key (configuration file only)", 10 ), SettingDef( "key_dig", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "dig key (configuration file only)", 10 ), SettingDef( "key_jet", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "jet key (configuration file only)", 10 ), SettingDef( "key_rope_on", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope on key (configuration file only)", 10 ), SettingDef( "key_rope_off", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope off key (configuration file only)", 10 ), SettingDef( "key_rope_in", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope in key (configuration file only)", 10 ), SettingDef( "key_rope_out", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope out key (configuration file only)", 10 ), SettingDef( "key_rope_rel", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "rope rel key (configuration file only)", 10 ), SettingDef( "key_screenshot", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "screenshot key (configuration file only)", 10 ), SettingDef( "key_debug", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "debug key (configuration file only)", 10 ), SettingDef( "key_dec_vol", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "decrease volume key (configuration file only)", 10 ), SettingDef( "key_inc_vol", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "increase volume key (configuration file only)", 10 ), SettingDef( "key_toggle_jukebox", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "toggle jukebox key (configuration file only)", 10 ), SettingDef( "key_weapon_1", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 1 key (configuration file only)", 10 ), SettingDef( "key_weapon_2", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 2 key (configuration file only)", 10 ), SettingDef( "key_weapon_3", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 3 key (configuration file only)", 10 ), SettingDef( "key_weapon_4", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 4 key (configuration file only)", 10 ), SettingDef( "key_weapon_5", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 5 key (configuration file only)", 10 ), SettingDef( "key_weapon_6", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 6 key (configuration file only)", 10 ), SettingDef( "key_weapon_7", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 7 key (configuration file only)", 10 ), SettingDef( "key_weapon_8", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 8 key (configuration file only)", 10 ), SettingDef( "key_weapon_9", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 9 key (configuration file only)", 10 ), SettingDef( "key_weapon_0", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "weapon 0 key (configuration file only)", 10 ), SettingDef( "key_spawn", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "spawn key (configuration file only)", 10 ), SettingDef( "key_next_weapon", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "next weapon key (configuration file only)", 10 ), SettingDef( "key_prev_weapon", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "previous weapon key (configuration file only)", 10 ), SettingDef( "key_insert_replay_bookmark", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "insert replay bookmark key (configuration file only)", 10 ), SettingDef( "key_change_focus", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "change focus key (configuration file only)", 10 ), SettingDef( "key_toggle_fullscreen", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "toggle fullscreen key (configuration file only)", 10 ), SettingDef( "key_show_fps", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "show frames per second key (configuration file only)", 10 ), SettingDef( "key_limit_fps", 0x0, Setting::STRING, 1, 1, STD_ATTR_CFO, "limit frames per second key (configuration file only)", 10 ), // empty SettingDef() for termination, do not remove SettingDef() }; // map for the setting "mode" SettingStringMap WopSettings:: m_mode_StringMap[NUMBER_OF_PROGRAM_MODES+1] = { SettingStringMap( "server", SERVER ), SettingStringMap( "client", CLIENT ), SettingStringMap( "replay", REPLAY ), SettingStringMap() }; // map for the setting "view" SettingStringMap WopSettings:: m_view_StringMap[NUMBER_OF_VIEW_MODES+1] = { SettingStringMap( "fullscreen", FULLSCREEN ), SettingStringMap( "window", WINDOW ), SettingStringMap() }; // map for the setting "view" SettingStringMap WopSettings:: m_gamemode_StringMap[NUMBER_OF_GAME_MODES+1] = { SettingStringMap( "deathmatch", DEATHMATCH ), SettingStringMap( "teamplay", TEAMPLAY ), SettingStringMap() }; // primary IDs of the settings, that are fixed on server side only // and sent to all clients. // IMPORTANT: All server settings must be present as objects on the // server, since we have this Client-Server-Join-or--Start-Game-Feature. // This creation of not explicitly set setting objects is done by // method WopSettings::enforcePresenceOfServerSettings and is not // adapted automatically to new entries in array m_serverSettingsIDs. // Therefore, whenever you add a new server setting, also add its // "instantiation" in WopSettings::enforcePresenceOfServerSettings. const char* WopSettings::m_serverSettingsIDs[] = { "gamemode", "width", "height", "nballs", "ngoals", "theme", NULL }; /********************************************************** * default values **********************************************************/ // IMPORTANT: Do not use paths containing '~' for HOME as // default paths. '~' is only expanded, if specified // in command line or configuration file. const int WopSettings::m_DefaultMode = CLIENT; const int WopSettings::m_DefaultGameMode = DEATHMATCH; const char WopSettings::m_DefaultData[] = "data"; const char WopSettings::m_DefaultServer[] = "localhost"; const int WopSettings::m_DefaultWidth = 1000; const int WopSettings::m_DefaultHeight = 1000; // RANDOM_CIRCLES_WOP_MAP_THEME defined in constants.hpp const char WopSettings::m_DefaultTheme[] = "standard"; // RANDOM_CIRCLES_WOP_MAP_THEME; const int WopSettings::m_DefaultView = WINDOW; const bool WopSettings::m_DefaultQuiet = false; #if defined(__APPLE__) || defined(WIN32) const char WopSettings::m_DefaultConfig[] = "woprc.txt"; #else const char WopSettings::m_DefaultConfig[] = ".woprc"; #endif const char WopSettings::m_DefaultName[] = "The Dude"; const Uint32 WopSettings::m_DefaultPlayerColor = 0x00ffffff; const int WopSettings::m_DefaultTeamID = 0; const char WopSettings::m_DefaultKeyboard[] = "us"; const int WopSettings::m_DefaultNumBalls = 2; const int WopSettings::m_DefaultNumGoals = 2; const real WopSettings::m_DefaultSZProb = 0.01; const bool WopSettings::m_DefaultDebug = false; // some additional definitions, e.g. for range control #define MIN_MAP_WIDTH 100 #define MAX_MAP_WIDTH 2000 #define MIN_MAP_HEIGHT 100 #define MAX_MAP_HEIGHT 2000 //#define MIN_GAME_ID 0 /**********************************************************/ #ifndef FUNCTION_ERROR #define FUNCTION_ERROR(fns) "\033[31m" << fns << ":\033[0m\n >> " #endif // FUNCTION_ERROR #ifndef CALLED_FROM #define CALLED_FROM(fns) " called from \033[36m" << fns << "\033[0m\n" #endif // CALLED_FROM /**********************************************************/ WopSettings* WopSettings::m_settings = 0; /**********************************************************/ WopSettings::WopSettings( const bool verbose ) : SettingDataBase( verbose ), m_writeConfigFile( false ), m_pickySuccess( false ) {} /**********************************************************/ WopSettings* WopSettings::getInstance() { if( NULL == m_settings && NULL == (m_settings = NEW WopSettings()) ) { cerr << FUNCTION_ERROR("WopSettings::getInstance") << "could not create setting object\n"; } return m_settings; } /**********************************************************/ void WopSettings::deleteInstance() { delete m_settings; m_settings = NULL; } /**********************************************************/ WopSettings::~WopSettings() { /* Deactivated writing of config file in the destructor, * since this will be triggered in the settings GUI from * now on (19.01.2005, uwe) * if( m_writeConfigFile ) { Setting *setting = getSetting( "config" ); String path( setting ? setting->getString() : NULL ); writeConfigFile( path ); } * */ } /**********************************************************/ int WopSettings::getMode( const bool exitOnError ) const { int mode = m_DefaultMode; // note that "mode" is a mandatory setting, and that we // have passed already SettingDataBase::finalCheck // ==> setting "mode" is present if( false == mapStringParameter("mode", m_mode_StringMap, mode) ) { Setting *setting = getSetting( "mode" ); if( setting ) { cerr << FUNCTION_ERROR("WopSettings::getMode") << "\"" << setting->getString() << "\" is not a valid program mode\n the list of " "options follows\n\n"; // print help strings only of level 0 printHelp( 0, 0 ); } if( exitOnError ) exit( 1 ); } return mode; } /**********************************************************/ int WopSettings::getGameMode() const { int gamemode = m_DefaultGameMode; Setting *setting = getSetting( "gamemode" ); if( setting != NULL ) { ASSERT( mapStringParameter("gamemode", m_gamemode_StringMap, gamemode), "WopSettings::getGameMode: \"%s\" is not a valid " "game mode.\n", setting->getString() ); } return gamemode; } /**********************************************************/ const char* WopSettings::getData() const { const char *data = m_DefaultData; Setting *setting = getSetting( "data" ); if( setting ) { ASSERT( true == setting->expandPathString(), "could not expand " "data path \"%s\"\n", setting->getString() ); data = setting->getString(); } return (const char*)data; } /**********************************************************/ const char* WopSettings::getServer() const { const char *server = m_DefaultServer; Setting *setting = getSetting( "server" ); if( setting ) server = setting->getString(); return (const char*)server; } /**********************************************************/ int WopSettings::getWidth() const { int width = m_DefaultWidth; Setting *setting = getSetting( "width" ); if( setting ) { width = setting->getInt(); if( width < MIN_MAP_WIDTH || width > MAX_MAP_WIDTH ) { cerr << FUNCTION_ERROR("WopSettings::getWidth") << "width (given as " << width << ") must be in " "range [" << MIN_MAP_WIDTH << ", " << MAX_MAP_WIDTH << "]\n"; exit( 1 ); } } return width; } /**********************************************************/ int WopSettings::getHeight() const { int height = m_DefaultHeight; Setting *setting = getSetting( "height" ); if( setting ) { height = setting->getInt(); if( height < MIN_MAP_HEIGHT || height > MAX_MAP_HEIGHT ) { cerr << FUNCTION_ERROR("WopSettings::getHeight") << "heigth (given as " << height << ") must be in " "range [" << MIN_MAP_HEIGHT << ", " << MAX_MAP_HEIGHT << "]\n"; exit( 1 ); } } return height; } /**********************************************************/ const char* WopSettings::getTheme() const { const Setting* setting = getSetting( "theme" ); if( setting ) return setting->getString(); return m_DefaultTheme; } /**********************************************************/ int WopSettings::getView() const { int view = m_DefaultView; if( NULL != getSetting("view") && false == mapStringParameter("view", m_view_StringMap, view) ) { cerr << FUNCTION_ERROR("WopSettings::getView") << "\"" << getSetting("view")->getString() << "\" is not a valid view\n the list of options" " follows\n\n"; // print help strings only of level 0 printHelp( 0, 0 ); exit( 1 ); } return view; } /**********************************************************/ bool WopSettings::getQuiet() const { bool quiet = m_DefaultQuiet; Setting *setting = getSetting( "quiet" ); if( setting ) { // quiet should be also usable as a simple flag // -> no parameter is interpreted as "true" quiet = setting->getNumParameters() > 0 ? setting->getBool() : true; // if the setting "quiet" is not specified explicitly, // the server runs in quiet mode explicitely } else { if( SERVER == getMode() ) quiet = true; } return quiet; } /**********************************************************/ const char* WopSettings::getName() const { const char *name = m_DefaultName; Setting *setting = getSetting( "name" ); if( setting ) name = setting->getString(); return (const char*) name; } /**********************************************************/ Uint32 WopSettings::getPlayerColor() const { Uint32 color = m_DefaultPlayerColor; Setting* setting = getSetting( "color" ); if( setting ) { color = (Uint32)(TPLCOL_RGB(setting->getInt(0), setting->getInt(1), setting->getInt(2))); } return color; } /**********************************************************/ int WopSettings::getTeamID() const { Setting *setting = getSetting( "team" ); if( setting ) { ASSERT( setting->getInt() >= 0 && setting->getInt() < MAX_NUMBER_OF_PLAYERS, "invalid team number (%i): 0 <= team < %i\n", setting->getInt(), MAX_NUMBER_OF_PLAYERS ); return setting->getInt(); } else { return m_DefaultTeamID; } } /**********************************************************/ const char* WopSettings::getKeyboard() const { const char *keyboard = m_DefaultKeyboard; Setting *setting = getSetting( "keyboard" ); if( setting ) keyboard = setting->getString(); return (const char*) keyboard; } /**********************************************************/ int WopSettings::getNumBalls() const { Setting *setting = getSetting( "nballs" ); if( setting ) { ASSERT( setting->getInt() >= 0, "the number of balls must be >= 0, using " "default %d\n",m_DefaultNumBalls ); return setting->getInt(); } else { return m_DefaultNumBalls; } } /**********************************************************/ int WopSettings::getNumGoals() const { Setting *setting = getSetting( "ngoals" ); if( setting ) { ASSERT( setting->getInt() >= 0, "the number of goals must be >= 0, using " "default %d\n",m_DefaultNumGoals ); return setting->getInt(); } else { return m_DefaultNumGoals; } } /**********************************************************/ real WopSettings::getSZProb() const { Setting *setting = getSetting( "szprob" ); if( setting ) { ASSERT( setting->getReal() >= 0.0 && setting->getReal() <= 1.0, "the skwoerm zone probability must be between " "0 and 1, using default %f\n", m_DefaultSZProb ); return setting->getReal(); } else { return m_DefaultSZProb; } } /**********************************************************/ bool WopSettings::getDebug() const { bool debug = m_DefaultDebug; Setting *setting = getSetting( "debug" ); if( setting ) { // debug should be also usable as a simple flag // -> no parameter is interpreted as "true" debug = setting->getNumParameters() > 0 ? setting->getBool() : true; } return debug; } /**********************************************************/ const char* WopSettings::getBots( const Sint32 i ) const { Setting *setting = getSetting( "bots" ); return (setting && i >= 0 && i < setting->getNumParameters()) ? setting->getString( i ) : 0x0; } /**********************************************************/ bool WopSettings::getReplayFileName( String& path ) const { Setting *setting = getSetting( "replaylog" ); if( setting ) { path = setting->getString(); return expandPath( path ); } return false; } /**********************************************************/ int WopSettings::getMinWidth() const { return MIN_MAP_WIDTH; } int WopSettings::getMaxWidth() const { return MAX_MAP_WIDTH; } int WopSettings::getMinHeight() const { return MIN_MAP_HEIGHT; } int WopSettings::getMaxHeight() const { return MIN_MAP_HEIGHT; } /**********************************************************/ bool WopSettings::getMusicDir( String& path ) const { Setting *setting = getSetting( "musicdir" ); if( setting ) { path = setting->getString(); return expandPath( path ); } return false; } /**********************************************************/ bool WopSettings::setGameMode( const int mode ) { int i = 0; while( m_gamemode_StringMap[i].m_String ) { if( m_gamemode_StringMap[i].m_Value == mode ) { if( !CHECK(setValue(m_basicSettings, "gamemode", &m_gamemode_StringMap[i].m_String), "WopSettings::setGameMode: failed setting " "the game mode to \"%s\"\n", m_gamemode_StringMap[i].m_String) ) { return false; } return true; } } return CHECK( false, "WopSettings::setGameMode: %d is not" "a valid game mode\n", mode ); } /**********************************************************/ bool WopSettings::setData( const char* const path ) { return CHECK( setValue(m_basicSettings, "data", (char**)&path), "WopSettings::setData: could not set data path " "to %s\n", path ); } /**********************************************************/ bool WopSettings::setServer( const char* const server ) { return CHECK( setValue(m_basicSettings, "server", (char**)&server), "WopSettings::setServer: could not set server address " "to \"%s\"", server ); } /**********************************************************/ bool WopSettings::setWidth( const int width ) { return CHECK( setValue(m_basicSettings, "width", &width), "WopSettings::setTeam: could not set map width to %d", width ); } /**********************************************************/ bool WopSettings::setHeight( const int height ) { return CHECK( setValue(m_basicSettings, "height", &height), "WopSettings::setTeam: could not set map height to %d", height ); } /**********************************************************/ bool WopSettings::setName( const char* const name ) { return CHECK( setValue(m_basicSettings, "name", (char**)&name), "WopSettings::setName: could not set player name " "to \"%s\"", name ); } /**********************************************************/ bool WopSettings::setPlayerColor( const Uint32 color ) { int rgb[3] = { TPLCOL_RED( color ), TPLCOL_GREEN( color ), TPLCOL_BLUE( color ) }; return CHECK( setValue(m_basicSettings, "color", (int*)rgb, -1, 3), "WopSettings::setColor: could not set player color " "to (%d,%d,%d)", rgb[0], rgb[1], rgb[2] ); } /**********************************************************/ bool WopSettings::setTeamID( const int teamID ) { return CHECK( setValue(m_basicSettings, "team", &teamID), "WopSettings::setTeam: could not set team ID to %d", teamID ); } /**********************************************************/ bool WopSettings::setKey( const char* name, const char* value ) { return CHECK( setValue(m_basicSettings, name, (char**)&value), "WopSettings::setKey: could not set key %s " "to value \"%s\"", name, value ); } /**********************************************************/ #define NUM_KEYBOARD_LAYOUTS 2 bool WopSettings::setKeyboard( const char* const layout ) { const char *layouts[NUM_KEYBOARD_LAYOUTS] = {"us", "de"}; for( int i = 0; i < NUM_KEYBOARD_LAYOUTS; i++ ) { if( !strcmp(layouts[i], layout) ) { return CHECK( setValue(m_basicSettings, "keyboard", (char**)&layout), "WopSettings::setKeyboard: could not set keyboard " "layout to \"%s\"", layout ); } } return CHECK( false, "WopSettings::setKeyboard: \"%s\" is not " "a valid keyboard layout", layout ); } #undef NUM_KEYBOARD_LAYOUTS /**********************************************************/ bool WopSettings::setNumBalls( const int nballs ) { return CHECK( setValue(m_basicSettings, "nballs", &nballs), "WopSettings::setNumBalls: could not set number " "of balls to %d", nballs ); } /**********************************************************/ bool WopSettings::setNumGoals( const int ngoals ) { return CHECK( setValue(m_basicSettings, "nballs", &ngoals), "WopSettings::setNumGoals: could not set number " "of goals to %d", ngoals ); } /**********************************************************/ bool WopSettings::isAvailableTheme( const String& name ) const { const char fn[] = "WopSettings::isAvailableTheme:"; PointerVector themes; ASSERT( getAvailableThemes(themes), "%s could not build list of theme names\n", fn ); for( int i = 0; i < themes.getSize(); i++ ) { if( *themes[i] == name ) return true; } return false; } /**********************************************************/ bool WopSettings:: getAvailableThemes( PointerVector& themes ) const { const char fn[] = "WopSettings::getAvailableThemes:"; themes.reset(); String themePath( getData() ); themePath += '/'; themePath += THEME_CONFIG_PATH; themePath += '/'; themePath += THEME_CONFIG; FILE* file = fopen( themePath, "rt" ); ASSERT( file, "%s could not open theme file \"%s\"\n", themePath.getString() ); char character = EOF ^ 0x1; const char *const key = SETTING_DB_DIRECTIVE_SECTION_IN; const int keyLength = strlen(key); int lineIndex = 1; while( character != EOF ) { // start scanning of line bool section_enter = true; for( int i = 0; i < keyLength; i++ ) { character = fgetc( file ); if( EOF == character || character != key[i] ) { section_enter = false; break; } } // We found the complete string "key" at top of the current // line. The position of "file" is the first character after // the directive "key". if( section_enter ) { // search start of theme string // NOTE: Due to the for-loop above we did not yet read // the first character after SETTING_DB_DIRECTIVE_SECTION_IN while( EOF != (character = fgetc(file)) ) { if( ! CHECK(character != '\n', "%s no section identifier in file \"%s\"," " line %d\n", fn, themePath.getString(), lineIndex) ) { fclose( file ); return false; } if( character != ' ' && character != '\t' ) break; } // build the theme name string String *name = NEW String; while( character != EOF && character != ' ' && character != '\t' && character != '\n' ) { *name += character; character = fgetc( file ); } // add theme name to list themes.append( name ); } // search the next line start if( character != '\n' ) { while( EOF != (character = fgetc(file)) && character != '\n' ); } lineIndex++; } // add the circle theme by hand themes.append( NEW String(RANDOM_CIRCLES_WOP_MAP_THEME) ); fclose( file ); return true; } /**********************************************************/ void WopSettings:: printAvailableThemes( ostream& out, const char *const head, const char *const tail ) const { const char fn[] = "WopSettings::printAvailableThemes:"; PointerVector themes; ASSERT( getAvailableThemes(themes), "%s could not build list of theme names\n", fn ); for( int i = 0; i < themes.getSize(); i++ ) { if( head ) out << head; out << *themes[i]; if( tail ) out << tail; } } /**********************************************************/ void WopSettings:: serializeServerSettings( Uint8*& bufferPointer ) { const Setting* setting = NULL; LOG(2) INFO( "WopSettings::serializeServerSettings\n" ); for( int i = 0; m_serverSettingsIDs[i]; i++ ) { if( NULL != (setting = getSetting(m_serverSettingsIDs[i])) ) { LOG(3) INFO( "\"%s\": -> present\n", m_serverSettingsIDs[i] ); // write a 1 to signalize, that a setting follows Serialize::serialize( (Uint8)1, bufferPointer ); // serialize the setting setting->serialize( bufferPointer ); } else { LOG(3) INFO( "\"%s\": -> absent\n", m_serverSettingsIDs[i] ); } } // terminate the serialization with a 0 Serialize::serialize( (Uint8)0, bufferPointer ); } /**********************************************************/ void WopSettings:: deserializeServerSettings( Uint8*& bufferPointer ) { const char fn[] = "WopSettings::deserializeServerSettings"; Setting* setting = NULL; Uint8 flag = 0; LOG(2) INFO( "%s\n", fn ); Serialize::deserialize( bufferPointer, flag ); while( flag ) { // create new setting object, if necessary ASSERT( !setting && NULL != (setting = new Setting(m_verbose)), "%s: could not create new Setting object\n", fn ); // deserialize the setting ASSERT( setting->deserialize(bufferPointer, m_basicSettings), "%s: server setting \"%s\" could not be deserialized " "properly\n", fn, setting->getIDString() ); // integrate the deserialized setting // Return value "false" means that the local setting // object has not been used and inserted in the list // of settings and can be reused if( integrateSetting(setting, true, NULL) ) { setting = NULL; } Serialize::deserialize( bufferPointer, flag ); } // if the last auxiliary setting has not been integrated // in the list of settings, delete it delete setting; } /**********************************************************/ void WopSettings::serialize( Uint8*& bufferPointer ) const { ASSERT( false, "WopSettings::serialize not implemented.\n" " Did you want to call WopSettings::" "serializeServerSettings instead?\n" ); } /**********************************************************/ void WopSettings::deserialize( Uint8*& bufferPointer ) { ASSERT( false, "WopSettings::deserialize not implemented.\n" " Did you want to call WopSettings::" "deserializeServerSettings instead?\n" ); } /**********************************************************/ Uint32 WopSettings::getSerializeBufferSize() const { Uint32 size = sizeof(Uint8); const Setting* setting = NULL; for( int i = 0; m_serverSettingsIDs[i]; i++ ) { if( NULL != (setting = getSetting(m_serverSettingsIDs[i])) ) { size += setting->getSerializeBufferSize(); size += sizeof(Uint8); } } return size; } /**********************************************************/ bool WopSettings::enforcePresenceOfServerSettings() { bool result = true; if( !getSetting("gamemode") ) { result = result && setGameMode( getGameMode() ); } if( !getSetting("width") ) { result = result && setWidth( getWidth() ); } if( !getSetting("height") ) { result = result && setHeight( getHeight() ); } if( !getSetting("nballs") ) { result = result && setNumBalls( getNumBalls() ); } if( !getSetting("ngoals") ) { result = result && setNumGoals( getNumGoals() ); } return result; } /********************************************************** * read settings form command line AND configuration file **********************************************************/ bool WopSettings::readSettings( const char** const CommandLine, const int nargs ) { const char fn[] = "WopSettings::readSettings"; bool pickySuccess = true; // read command line (SettingDataBase::readSettings) LOG(1) INFO( "%s: reading command line options\n", fn ); // read settings from command line if( false == SettingDataBase::readSettings( m_basicSettings, // class internal setting definitions CommandLine, nargs, // command line and # argumentes true, // overwrite == true false, // expectDashesInCommandLine == false &pickySuccess) || // get more details about the success // stop also, if there happened only small errors false == pickySuccess ) { return false; } String configfile; int rank = 0; // read the configuration files in the ranking order while( getConfigFilePath(configfile, rank) ) { LOG(1) INFO( "%s: checking presence of \"%s\"\n", fn, configfile.getString() ); // check, whether the configuration file is available FILE* configFile = fopen( (char*)configfile, "r" ); if( configFile == NULL ) { // If the file, that was specified in the command // line cannot be opened, abort. if( !CHECK( rank > 0, "%s: cannot open \"%s\"\n", fn, configfile.getString() ) ) return false; // otherwise only print out a warning LOG(1) INFO( "%s: \"%s\" not available\n", fn, configfile.getString() ); continue; } fclose( configFile ); // now we are sure that we can open the configuration file LOG(1) INFO( "%s: reading options in \"%s\"\n", fn, configfile.getString() ); // read configuration file, DO NOT overwrite settings, since // the command line has got a higher priority ( 3rd param == false) if( ! SettingDataBase:: readSettings(m_basicSettings, configfile, false, &pickySuccess) || // stop also, if there happened only small errors ! pickySuccess ) { return false; } } // check, if every mandatory setting is present if( false == finalCheck(m_basicSettings) ) return false; // check if data directory is present by opening the theme path String themesConfPath( getData() ); themesConfPath += '/'; themesConfPath += THEME_CONFIG_PATH; themesConfPath += '/'; themesConfPath += THEME_CONFIG; FILE* themesConfFile = fopen( themesConfPath, "r" ); ASSERT ( themesConfFile, "Could not open data directory (%s).\n" "Make sure that you:\n" " - downloaded the data package\n" " - set the \"data\" option in the command line " "or the configuration file\n", getData() ); fclose( themesConfFile ); // remove pure server settings from a client // if( getMode() == CLIENT ) { // for( int i = 0; m_serverSettingsIDs[i]; i++ ) { // Setting *setting = getSetting( m_serverSettingsIDs[i] ); // if( setting ) { // LOG(1) INFO( "%s: setting \"%s\" ignored on client\n", // fn, m_serverSettingsIDs[i] ); // delSetting( m_serverSettingsIDs[i] ); // } // } // } // check the validity of the theme right here // In fact it is checked later per default in World::createFromTheme, // but after a lot of output. To check it right at the program start // after reading the settings is more user friendly. if( getTheme() ) { if( ! CHECK(isAvailableTheme(getTheme()), "%s \"%s\" is not a valid theme name.\n" " =======================\n" " Available Themes:\n", fn, getTheme()) ) { // if theme name is not valid, print list of available themes printAvailableThemes( std::cerr, " \033[0;1m- ", "\033[0m\n" ); std::cout <<" ======================="<getString(); rank++; return expandPath( path ); // otherwise get the first path in the hierarchy } else { return getConfigFilePath( path, ++rank ); } // otherwise take the next path in the hierarchy } else { if( m_configPaths[rank-1] == NULL ) return false; path = m_configPaths[(rank++)-1]; } return expandPath( path ); } /**********************************************************/ bool WopSettings::expandPath( String& path ) const { const char fn[] = "WopSettings::expandPath:"; // check, if there is something to expand: a path must // contain at least '~' and a following character to be // sensibly expanded if( path.getLength() < 2 || path[0] != '~' ) return true; #ifdef NO_HOME_PATH_EXPANSION return CHECK( false, "%s path expansion is disabled. Cannot expand " "path \"%s\"\n", fn, path.getString() ); #else // '~/' means "home of current user String expansion; int iNull = 1; if( path[1] == '/' ) { expansion = getenv( "HOME" ); if( !CHECK( expansion.getLength(), "%s could not get content of environment " "variable \"HOME\"", fn ) ) { return false; } // '~' is not followed by a '/', so we need the full path // of a different user's home directory } else { // temporarily crop the user's name while( path[iNull] != '/' && path[iNull] != 0 ) iNull++; // remember, whether you have cropped the string const bool cropped = (path[iNull] == '/'); path[iNull] = 0; // get the user's full home path from /etc/pwd struct passwd *pwdentry = getpwnam( path.getString()+1 ); if( !CHECK( pwdentry != NULL, "%s could not read passwd entry of user \"%s\"", fn, path.getString()+1 ) ) { // restore path if( cropped ) path[iNull] = '/'; return false; } expansion = pwdentry->pw_dir; if( !CHECK( expansion.getLength(), "%s could not expand home path of user \"%s\"", fn, path.getString()+1 ) ) { // restore path if( cropped ) path[iNull] = '/'; return false; } if( cropped ) path[iNull] = '/'; } // concatenate expansion and string String extention( path.getString() + iNull ); path = expansion; path += extention; #endif return true; } /**********************************************************/ bool WopSettings::writeConfigFile( String& path ) { const char fn[] = "WopSettings::writeConfigFile"; // get default path, if "path" is empty if( !path.getLength() ) { // if config file is given write to that location else try to // write in the current directory if ( !WopSettings::getInstance()->getSetting( "config" ) ) { int rank = 0; WopSettings::getInstance()->getConfigFilePath( path, rank ); } else { path = getenv( "HOME" ); if( path.getLength() ) { path += '/'; path += m_DefaultConfig; } else path = "woprc.txt"; } } INFO( "WopSettings::writeConfigFile: writing configuration " "file to %s\n", path.getLength() ? path.getString() : "woprc.txt" ); // open file FILE *file = fopen( path, "wt" ); if( file == NULL ) { cerr << FUNCTION_ERROR(fn) << "cannot open \"" << path << "\"\n"; return false; } // write file fprintf( file, "## This is a skeleton file for the configuration of\n" "## \"Woerms of Prey\", www.wormsofprey.org\n" "##\n" "## - All settings, used here, can also be specified\n" "## using the command line. For example\n" "## \"wop mode=client view=window\" (no spaces between\n" "## the setting's name and its values)\n" "## Command line settings OVERWRITE settings read from\n" "## configuration files.\n" "## - For a quick overview of the possible settings\n" "## invoke \"wop -help\"\n" "## - On UNIX based systems the shortcuts using \"~\"\n" "## are expanded correctly.\n" "## - If you want to use the default value of a setting,\n" "## you can leave it undefined. The default is taken\n" "## automatically.\n" "## - On the command line you can specify the path of\n" "## this configuration file using the setting \"config\"\n" "\n" ); fprintf( file, "## program mode [server,client,replay]\n" "## - required\n" "mode = client\n" "\n" ); fprintf( file, "## game mode [deathmatch,teamplay]\n" "## - optional, default \"deathmatch\"\n" "## - has effect on \"server\" only\n" "gamemode = %s\n" "\n", m_gamemode_StringMap[getGameMode()].m_String ); fprintf( file, "## path of the data directory\n" "## - optional, default \"./data\"\n" "data = %s\n" "\n", getData() ); fprintf( file, "## server name or IP address\n" "## - optional, default \"localhost\"\n" "## - can also be chosen in the menu\n" "# server = localhost\n" "\n" ); fprintf( file, "## width of the world map\n" "## - optional, default 1000\n" "## - has effect on \"server\" only\n" "## - a selected theme may overwrite \"width\"\n" "width = %d\n" "\n", getWidth() ); fprintf( file, "## height of the world map\n" "## - optional, default 1000\n" "## - has effect on server only\n" "## - a selected theme may overwrite \"height\"\n" "height = %d\n" "\n", getHeight() ); fprintf( file, "## theme of the world's map\n" "## [standard, medium, sparse1, fossil, circle,\n" "## randomcircles]\n" "## - optional, default \"standard\"\n" "## - has effect on \"server\" only\n" "## - for a list of available themes have a look\n" "## in the configuration file\n" "## /images/themes.conf\n" "## [standard, medium,\n" "## sparse1, fossil,\n" "## circle, sky, randomcircles]\n" "theme = %s\n" "\n", getTheme() ); fprintf( file, "## view mode [window,fullscreen]\n" "## - optional, default \"window\"\n" "## - has effect on \"client\" and \"replay\" only\n" "view = %s\n" "\n", m_view_StringMap[getView()].m_String ); fprintf( file, "## quiet [yes,no]\n" "## - default on \"client\" is \"no\"\n" "## - default on \"server\" is \"yes\"\n" "## - switches off/on sound\n" "quiet = %s\n" "\n", getQuiet() ? "yes" : "no" ); fprintf( file, "## player name\n" "## - has effect on \"client\" only\n" "## - default \"The Dude\"\n" "## - can also be specified in option menu\n" "name = \"%s\"\n" "\n", getName() ); fprintf( file, "## avatar color\n" "## - default \"255,255,255\"\n" "## - has effect on \"client\" only\n" "## - specifies the RED,GREEN,BLUE contributions\n" "## to the player's color (three comma separated\n" "## values from 0 to 255\n" "## - can also be specified in option menu\n" "color = %d,%d,%d\n" "\n", TPLCOL_RED( getPlayerColor() ), TPLCOL_GREEN( getPlayerColor() ), TPLCOL_BLUE( getPlayerColor() ) ); fprintf( file, "## the team number [0,...,20]\n" "## - default 0\n" "## - has effect on \"client\" only, and only, if the\n" "## gamemode is \"teamplay\"\n" "## - can also be specified in option menu\n" "team = %d\n" "\n", getTeamID() ); fprintf( file, "## keyboard layout [us,de]\n" "## - default \"us\"\n" "## - this setting generally affects, whether the rope\n" "## is triggered by \"z\" (us) or by \"y\" (de)\n" "keyboard = %s\n" "\n", getKeyboard() ); fprintf( file, "## number of balls in the game\n" "## - default 2\n" "## - has effect on \"server\" only\n" "nballs = %d\n" "\n", getNumBalls() ); fprintf( file, "## number of goals in the game\n" "## - default 2\n" "## - has effect on \"server\" only\n" "ngoals = %d\n" "\n", getNumGoals() ); fprintf( file, "## probability for creating skwoerm zones [0.0,1.0]\n" "## - default 0.01\n" "## - has effect on \"server\" only\n" "szprob = %f\n" "\n", getSZProb() ); fprintf( file, "## debug [yes,no]\n" "## - default: no\n" "## - warning: enabling turns off the SDL parachute!\n" "debug = %s\n" "\n", getDebug() ? "yes" : "no" ); fprintf( file, "## juke box directory\n" "## - no default\n" "## - this setting specifies a directory where WoP\n" "## will search files of type \".wav\", \".ogg\", \".mp3\", \".mid\", \".mod\".\n" "## These files are played in random order as\n" "## background tunes during the game.\n" "## - CONTROL in the game:\n" "## F5 : volume -\n" "## F6 : volume +\n" "## F7 : toggle music on/off\n" "# musicdir = \"~/mymusic/woptracks/smooth tracks\"\n" "## on the command line you would have to write\n" "## musicdir=\"~/mymusic/woptracks/smooth\\ tracks\"\n" ); String musicDir; if ( getMusicDir( musicDir ) ) fprintf( file, "musicdir = %s\n", musicDir.getString() ); fprintf( file, "\n" "## key configuration\n" "## - can also be changed in option menu\n" "key_left = \"%s\"\n" "key_right = \"%s\"\n" "key_up = \"%s\"\n" "key_down = \"%s\"\n" "key_shoot = \"%s\"\n" "key_jump = \"%s\"\n" "key_dig = \"%s\"\n" "key_jet = \"%s\"\n" "key_rope_on = \"%s\"\n" "key_rope_off = \"%s\"\n" "key_rope_in = \"%s\"\n" "key_rope_out = \"%s\"\n" "key_rope_rel = \"%s\"\n" "key_screenshot = \"%s\"\n" "key_debug = \"%s\"\n" "key_dec_vol = \"%s\"\n" "key_inc_vol = \"%s\"\n" "key_toggle_jukebox = \"%s\"\n" "key_weapon_1 = \"%s\"\n" "key_weapon_2 = \"%s\"\n" "key_weapon_3 = \"%s\"\n" "key_weapon_4 = \"%s\"\n" "key_weapon_5 = \"%s\"\n" "key_weapon_6 = \"%s\"\n" "key_weapon_7 = \"%s\"\n" "key_weapon_8 = \"%s\"\n" "key_weapon_9 = \"%s\"\n" "key_weapon_0 = \"%s\"\n" "key_spawn = \"%s\"\n" "key_next_weapon = \"%s\"\n" "key_prev_weapon = \"%s\"\n" "key_insert_replay_bookmark = \"%s\"\n" "key_change_focus = \"%s\"\n" "key_toggle_fullscreen = \"%s\"\n" "key_show_fps = \"%s\"\n" "key_limit_fps = \"%s\"\n", getSetting( "key_left" )->getString(), getSetting( "key_right" )->getString(), getSetting( "key_up" )->getString(), getSetting( "key_down" )->getString(), getSetting( "key_shoot" )->getString(), getSetting( "key_jump" )->getString(), getSetting( "key_dig" )->getString(), getSetting( "key_jet" )->getString(), getSetting( "key_rope_on" )->getString(), getSetting( "key_rope_off" )->getString(), getSetting( "key_rope_in" )->getString(), getSetting( "key_rope_out" )->getString(), getSetting( "key_rope_rel" )->getString(), getSetting( "key_screenshot" )->getString(), getSetting( "key_debug" )->getString(), getSetting( "key_dec_vol" )->getString(), getSetting( "key_inc_vol" )->getString(), getSetting( "key_toggle_jukebox" )->getString(), getSetting( "key_weapon_1" )->getString(), getSetting( "key_weapon_2" )->getString(), getSetting( "key_weapon_3" )->getString(), getSetting( "key_weapon_4" )->getString(), getSetting( "key_weapon_5" )->getString(), getSetting( "key_weapon_6" )->getString(), getSetting( "key_weapon_7" )->getString(), getSetting( "key_weapon_8" )->getString(), getSetting( "key_weapon_9" )->getString(), getSetting( "key_weapon_0" )->getString(), getSetting( "key_spawn" )->getString(), getSetting( "key_next_weapon" )->getString(), getSetting( "key_prev_weapon" )->getString(), getSetting( "key_insert_replay_bookmark" )->getString(), getSetting( "key_change_focus" )->getString(), getSetting( "key_toggle_fullscreen" )->getString(), getSetting( "key_show_fps" )->getString(), getSetting( "key_limit_fps" )->getString() ); fclose( file ); return true; } /**********************************************************/ void WopSettings::printHelp( const int lowerHelpLevel, const int upperHelpLevel ) const { SettingDataBase::printHelp( m_basicSettings, lowerHelpLevel, upperHelpLevel ); } /**********************************************************/