Legends-of-Azeroth-Pandaria-5.4.8

Форк
0
340 строк · 9.9 Кб
1
/*
2
* This file is part of the Pandaria 5.4.8 Project. See THANKS file for Copyright information
3
*
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License as published by the
6
* Free Software Foundation; either version 2 of the License, or (at your
7
* option) any later version.
8
*
9
* This program is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12
* more details.
13
*
14
* You should have received a copy of the GNU General Public License along
15
* with this program. If not, see <http://www.gnu.org/licenses/>.
16
*/
17

18
#include "Config.h"
19
#include "Log.h"
20
#include "StringConvert.h"
21
#include "Util.h"
22
#include <boost/property_tree/ini_parser.hpp>
23
#include <algorithm>
24
#include <cstdlib>
25
#include <memory>
26
#include <mutex>
27

28
namespace bpt = boost::property_tree;
29

30
namespace
31
{
32
    std::string _filename;
33
    std::vector<std::string> _additonalFiles;
34
    std::vector<std::string> _args;
35
    bpt::ptree _config;
36
    std::mutex _configLock;
37

38
    bool LoadFile(std::string const& file, bpt::ptree& fullTree, std::string& error)
39
    {
40
        try
41
        {
42
            bpt::ini_parser::read_ini(file, fullTree);
43

44
            if (fullTree.empty())
45
            {
46
                error = "empty file (" + file + ")";
47
                return false;
48
            }
49
        }
50
        catch (bpt::ini_parser::ini_parser_error const& e)
51
        {
52
            if (e.line() == 0)
53
                error = e.message() + " (" + e.filename() + ")";
54
            else
55
                error = e.message() + " (" + e.filename() + ":" + std::to_string(e.line()) + ")";
56
            return false;
57
        }
58

59
        return true;
60
    }
61

62
    // Converts ini keys to the environment variable key (upper snake case).
63
    // Example of conversions:
64
    //   SomeConfig => SOME_CONFIG
65
    //   myNestedConfig.opt1 => MY_NESTED_CONFIG_OPT_1
66
    //   LogDB.Opt.ClearTime => LOG_DB_OPT_CLEAR_TIME
67
    std::string IniKeyToEnvVarKey(std::string const& key)
68
    {
69
        std::string result;
70

71
        const char *str = key.c_str();
72
        size_t n = key.length();
73

74
        char curr;
75
        bool isEnd;
76
        bool nextIsUpper;
77
        bool currIsNumeric;
78
        bool nextIsNumeric;
79

80
        for (size_t i = 0; i < n; ++i)
81
        {
82
            curr = str[i];
83
            if (curr == ' ' || curr == '.' || curr == '-')
84
            {
85
                result += '_';
86
                continue;
87
            }
88

89
            isEnd = i == n - 1;
90
            if (!isEnd)
91
            {
92
                nextIsUpper = isupper(str[i + 1]);
93

94
                // handle "aB" to "A_B"
95
                if (!isupper(curr) && nextIsUpper)
96
                {
97
                    result += static_cast<char>(std::toupper(curr));
98
                    result += '_';
99
                    continue;
100
                }
101

102
                currIsNumeric = isNumeric(curr);
103
                nextIsNumeric = isNumeric(str[i + 1]);
104

105
                // handle "a1" to "a_1"
106
                if (!currIsNumeric && nextIsNumeric)
107
                {
108
                    result += static_cast<char>(std::toupper(curr));
109
                    result += '_';
110
                    continue;
111
                }
112

113
                // handle "1a" to "1_a"
114
                if (currIsNumeric && !nextIsNumeric)
115
                {
116
                    result += static_cast<char>(std::toupper(curr));
117
                    result += '_';
118
                    continue;
119
                }
120
            }
121

122
            result += static_cast<char>(std::toupper(curr));
123
        }
124
        return result;
125
    }
126

127
    Optional<std::string> EnvVarForIniKey(std::string const& key)
128
    {
129
        std::string envKey = "TC_" + IniKeyToEnvVarKey(key);
130
        char* val = std::getenv(envKey.c_str());
131
        if (!val)
132
            return std::nullopt;
133

134
        return std::string(val);
135
    }    
136
}
137

138
bool ConfigMgr::LoadInitial(std::string file, std::vector<std::string> args,
139
                            std::string& error)
140
{
141
    std::lock_guard<std::mutex> lock(_configLock);
142

143
    _filename = std::move(file);
144
    _args = std::move(args);
145

146
    bpt::ptree fullTree;
147
    if (!LoadFile(_filename, fullTree, error))
148
        return false;
149

150
    // Since we're using only one section per config file, we skip the section and have direct property access
151
    _config = fullTree.begin()->second;
152

153
    return true;
154
}
155

156
bool ConfigMgr::LoadAdditionalFile(std::string file, bool keepOnReload, std::string& error)
157
{
158
    bpt::ptree fullTree;
159
    if (!LoadFile(file, fullTree, error))
160
        return false;
161

162
    for (bpt::ptree::value_type const& child : fullTree.begin()->second)
163
        _config.put_child(bpt::ptree::path_type(child.first, '/'), child.second);
164

165
    if (keepOnReload)
166
        _additonalFiles.emplace_back(std::move(file));
167

168
    return true;
169
}
170

171
std::vector<std::string> ConfigMgr::OverrideWithEnvVariablesIfAny()
172
{
173
    std::lock_guard<std::mutex> lock(_configLock);
174

175
    std::vector<std::string> overriddenKeys;
176

177
    for (bpt::ptree::value_type& itr: _config)
178
    {
179
        if (!itr.second.empty() || itr.first.empty())
180
            continue;
181

182
        Optional<std::string> envVar = EnvVarForIniKey(itr.first);
183
        if (!envVar)
184
            continue;
185

186
        itr.second = bpt::ptree(*envVar);
187

188
        overriddenKeys.push_back(itr.first);
189
    }
190

191
    return overriddenKeys;
192
}
193

194
ConfigMgr* ConfigMgr::instance()
195
{
196
    static ConfigMgr instance;
197
    return &instance;
198
}
199

200
bool ConfigMgr::Reload(std::vector<std::string>& errors)
201
{
202
    std::string error;
203
    if (!LoadInitial(_filename, std::move(_args), error))
204
        errors.push_back(std::move(error));
205

206
    for (std::string const& additionalFile : _additonalFiles)
207
        if (!LoadAdditionalFile(additionalFile, false, error))
208
            errors.push_back(std::move(error));
209

210
    OverrideWithEnvVariablesIfAny();
211

212
    return errors.empty();
213
}
214

215
template<class T>
216
T ConfigMgr::GetValueDefault(std::string const& name, T def, bool quiet) const
217
{
218
    try
219
    {
220
        return _config.get<T>(bpt::ptree::path_type(name, '/'));
221
    }
222
    catch (bpt::ptree_bad_path const&)
223
    {
224
        Optional<std::string> envVar = EnvVarForIniKey(name);
225
        if (envVar)
226
        {
227
            Optional<T> castedVar = Trinity::StringTo<T>(*envVar);
228
            if (!castedVar)
229
            {
230
                TC_LOG_ERROR("server.loading", "Bad value defined for name %s in environment variables, going to use default instead", name.c_str());
231
                return def;
232
            }
233

234
            if (!quiet)
235
                TC_LOG_WARN("server.loading", "Missing name %s in config file %s, recovered with environment '%s' value.", name.c_str(), _filename.c_str(), envVar->c_str());
236

237
            return *castedVar;
238
        }
239
        else if (!quiet)
240
        {
241
            TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file",
242
                name.c_str(), _filename.c_str(), name.c_str(), std::to_string(def).c_str());
243
        }
244
    }
245
    catch (bpt::ptree_bad_data const&)
246
    {
247
        TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead",
248
            name.c_str(), _filename.c_str(), std::to_string(def).c_str());
249
    }
250

251
    return def;
252
}
253

254
template<>
255
std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string def, bool quiet) const
256
{
257
    try
258
    {
259
        return _config.get<std::string>(bpt::ptree::path_type(name, '/'));
260
    }
261
    catch (bpt::ptree_bad_path const&)
262
    {
263
        Optional<std::string> envVar = EnvVarForIniKey(name);
264
        if (envVar)
265
        {
266
            if (!quiet)
267
                TC_LOG_WARN("server.loading", "Missing name %s in config file %s, recovered with environment '%s' value.", name.c_str(), _filename.c_str(), envVar->c_str());
268

269
            return *envVar;
270
        }
271
        else if (!quiet)
272
        {
273
            TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file",
274
                name.c_str(), _filename.c_str(), name.c_str(), def.c_str());
275
        }
276
    }
277
    catch (bpt::ptree_bad_data const&)
278
    {
279
        TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use %s instead",
280
            name.c_str(), _filename.c_str(), def.c_str());
281
    }
282

283
    return def;
284
}
285

286
std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def, bool quiet) const
287
{
288
    std::string val = GetValueDefault(name, def, quiet);
289
    val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
290
    return val;
291
}
292

293
bool ConfigMgr::GetBoolDefault(std::string const& name, bool def, bool quiet) const
294
{
295
    std::string val = GetValueDefault(name, std::string(def ? "1" : "0"), quiet);
296
    val.erase(std::remove(val.begin(), val.end(), '"'), val.end());
297
    Optional<bool> boolVal = Trinity::StringTo<bool>(val);
298
    if (boolVal)
299
        return *boolVal;
300
    else
301
    {
302
        TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use '%s' instead",
303
            name.c_str(), _filename.c_str(), def ? "true" : "false");
304
        return def;
305
    }
306
}
307

308
int ConfigMgr::GetIntDefault(std::string const& name, int def, bool quiet) const
309
{
310
    return GetValueDefault(name, def, quiet);
311
}
312

313
float ConfigMgr::GetFloatDefault(std::string const& name, float def, bool quiet) const
314
{
315
    return GetValueDefault(name, def, quiet);
316
}
317

318
std::string const& ConfigMgr::GetFilename()
319
{
320
    std::lock_guard<std::mutex> lock(_configLock);
321
    return _filename;
322
}
323

324
std::vector<std::string> const& ConfigMgr::GetArguments() const
325
{
326
    return _args;
327
}
328

329
std::vector<std::string> ConfigMgr::GetKeysByString(std::string const& name)
330
{
331
    std::lock_guard<std::mutex> lock(_configLock);
332

333
    std::vector<std::string> keys;
334

335
    for (bpt::ptree::value_type const& child : _config)
336
        if (child.first.compare(0, name.length(), name) == 0)
337
            keys.push_back(child.first);
338

339
    return keys;
340
}
341

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.