/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2 * Copyright (C) 2021+ WarheadCore */ #include "Config.h" #include "Log.h" #include "StringConvert.h" #include "StringFormat.h" #include "Util.h" #include #include #include namespace { std::string _filename; std::vector _additonalFiles; std::vector _args; std::unordered_map _configOptions; std::mutex _configLock; void AddKey(std::string const& optionName, std::string const& optionKey, bool replace = true) { auto const& itr = _configOptions.find(optionName); if (itr != _configOptions.end()) { if (!replace) { sLog->outError("> Config: Option '%s' is exist! Option key - '%s'", optionName.c_str(), itr->second.c_str()); return; } _configOptions.erase(optionName); } _configOptions.emplace(optionName, optionKey); } void ParseFile(std::string const& file) { std::ifstream in(file); if (in.fail()) throw ConfigException(acore::StringFormat("Config::LoadFile: Failed open file '%s'", file.c_str())); uint32 count = 0; while (in.good()) { std::string line; std::getline(in, line); if (line.empty()) continue; line = acore::String::Trim(line, in.getloc()); // comments if (line[0] == '#' || line[0] == '[') continue; size_t found = line.find_first_of('#'); if (found != std::string::npos) line = line.substr(0, found); auto const equal_pos = line.find('='); if (equal_pos == std::string::npos || equal_pos == line.length()) return; auto entry = acore::String::Trim(line.substr(0, equal_pos), in.getloc()); auto value = acore::String::Trim(line.substr(equal_pos + 1), in.getloc()); value.erase(std::remove(value.begin(), value.end(), '"'), value.end()); AddKey(entry, value); count++; } if (!count) throw ConfigException(acore::StringFormat("Config::LoadFile: Empty file '%s'", file.c_str())); } bool LoadFile(std::string const& file) { try { ParseFile(file); return true; } catch (const std::exception& e) { sLog->outError("> Config: %s", e.what()); } return false; } } bool ConfigMgr::LoadInitial(std::string const& file) { std::lock_guard lock(_configLock); _configOptions.clear(); return LoadFile(file); } bool ConfigMgr::LoadAdditionalFile(std::string file) { std::lock_guard lock(_configLock); return LoadFile(file); } ConfigMgr* ConfigMgr::instance() { static ConfigMgr instance; return &instance; } bool ConfigMgr::Reload() { if (!LoadAppConfigs()) return false; return LoadModulesConfigs(); } template T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLogs /*= true*/) const { auto const& itr = _configOptions.find(name); if (itr == _configOptions.end()) { if (showLogs) { sLog->outError("> Config: Missing name %s in config, add \"%s = %s\"", name.c_str(), name.c_str(), acore::ToString(def).c_str()); } return def; } auto value = acore::StringTo(itr->second); if (!value) { if (showLogs) { sLog->outError("> Config: Bad value defined for name '%s', going to use '%s' instead", name.c_str(), acore::ToString(def).c_str()); } return def; } return *value; } template<> std::string ConfigMgr::GetValueDefault(std::string const& name, std::string const& def, bool showLogs /*= true*/) const { auto const& itr = _configOptions.find(name); if (itr == _configOptions.end()) { if (showLogs) { sLog->outError("> Config: Missing name %s in config, add \"%s = %s\"", name.c_str(), name.c_str(), def.c_str()); } return def; } return itr->second; } template T ConfigMgr::GetOption(std::string const& name, T const& def, bool showLogs /*= true*/) const { return GetValueDefault(name, def, showLogs); } template<> bool ConfigMgr::GetOption(std::string const& name, bool const& def, bool showLogs /*= true*/) const { std::string val = GetValueDefault(name, std::string(def ? "1" : "0"), showLogs); auto boolVal = acore::StringTo(val); if (!boolVal) { if (showLogs) { sLog->outError("> Config: Bad value defined for name '%s', going to use '%s' instead", name.c_str(), def ? "true" : "false"); } return def; } return *boolVal; } std::vector ConfigMgr::GetKeysByString(std::string const& name) { std::lock_guard lock(_configLock); std::vector keys; for (auto const& [optionName, key] : _configOptions) if (!optionName.compare(0, name.length(), name)) keys.emplace_back(optionName); return keys; } std::string const& ConfigMgr::GetFilename() { std::lock_guard lock(_configLock); return _filename; } std::vector const& ConfigMgr::GetArguments() const { return _args; } std::string const ConfigMgr::GetConfigPath() { std::lock_guard lock(_configLock); #if AC_PLATFORM == AC_PLATFORM_WINDOWS return "configs/"; #else return std::string(_CONF_DIR) + "/"; #endif } void ConfigMgr::Configure(std::string const& initFileName, std::vector args, std::string const& modulesConfigList /*= ""*/) { _filename = initFileName; _args = std::move(args); // Add modules config if exist if (!modulesConfigList.empty()) { Tokenizer configFileList(modulesConfigList, ','); for (auto const& itr : configFileList) _additonalFiles.emplace_back(std::string(itr)); } } bool ConfigMgr::LoadAppConfigs() { // #1 - Load init config file .conf.dist if (!LoadInitial(_filename + ".dist")) return false; // #2 - Load .conf file if (!LoadAdditionalFile(_filename)) return false; return true; } bool ConfigMgr::LoadModulesConfigs() { if (_additonalFiles.empty()) return true; // Start loading module configs std::vector moduleConfigFiles; std::string const& moduleConfigPath = GetConfigPath() + "modules/"; bool isExistDefaultConfig = true; bool isExistDistConfig = true; for (auto const& distFileName : _additonalFiles) { std::string defaultFileName = distFileName; if (!defaultFileName.empty()) defaultFileName.erase(defaultFileName.end() - 5, defaultFileName.end()); // Load .conf.dist config if (!LoadAdditionalFile(moduleConfigPath + distFileName)) isExistDistConfig = false; // Load .conf config if (!LoadAdditionalFile(moduleConfigPath + defaultFileName)) isExistDefaultConfig = false; if (isExistDefaultConfig && isExistDistConfig) moduleConfigFiles.emplace_back(defaultFileName); else if (!isExistDefaultConfig && isExistDistConfig) moduleConfigFiles.emplace_back(distFileName); } // If module configs not exist - no load if (moduleConfigFiles.empty()) return false; // Print modules configurations sLog->outString(); sLog->outString("Using modules configuration:"); for (auto const& itr : moduleConfigFiles) sLog->outString("> %s", itr.c_str()); sLog->outString(""); return true; } /* * Deprecated geters. This geters will be deleted */ // @deprecated DO NOT USE - use GetOption instead. std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def, bool showLogs /*= true*/) { return GetOption(name, def, showLogs); } // @deprecated DO NOT USE - use GetOption instead. bool ConfigMgr::GetBoolDefault(std::string const& name, bool def, bool showLogs /*= true*/) { return GetOption(name, def, showLogs); } // @deprecated DO NOT USE - use GetOption instead. int ConfigMgr::GetIntDefault(std::string const& name, int def, bool showLogs /*= true*/) { return GetOption(name, def, showLogs); } // @deprecated DO NOT USE - use GetOption instead. float ConfigMgr::GetFloatDefault(std::string const& name, float def, bool showLogs /*= true*/) { return GetOption(name, def, showLogs); } /* * End deprecated geters */ #define TEMPLATE_CONFIG_OPTION(__typename) \ template __typename ConfigMgr::GetOption<__typename>(std::string const& name, __typename const& def, bool showLogs /*= true*/) const; TEMPLATE_CONFIG_OPTION(std::string) TEMPLATE_CONFIG_OPTION(uint8) TEMPLATE_CONFIG_OPTION(int8) TEMPLATE_CONFIG_OPTION(uint16) TEMPLATE_CONFIG_OPTION(int16) TEMPLATE_CONFIG_OPTION(uint32) TEMPLATE_CONFIG_OPTION(int32) TEMPLATE_CONFIG_OPTION(uint64) TEMPLATE_CONFIG_OPTION(int64) TEMPLATE_CONFIG_OPTION(float) #undef TEMPLATE_CONFIG_OPTION