Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include "settings.h"
21 : #include "irrlichttypes_bloated.h"
22 : #include "exceptions.h"
23 : #include "jthread/jmutexautolock.h"
24 : #include "strfnd.h"
25 : #include <iostream>
26 : #include <fstream>
27 : #include <sstream>
28 : #include "debug.h"
29 : #include "log.h"
30 : #include "util/serialize.h"
31 : #include "filesys.h"
32 : #include "noise.h"
33 : #include <cctype>
34 : #include <algorithm>
35 :
36 1 : static Settings main_settings;
37 : Settings *g_settings = &main_settings;
38 1 : std::string g_settings_path;
39 :
40 26 : Settings::~Settings()
41 : {
42 13 : clear();
43 13 : }
44 :
45 :
46 0 : Settings & Settings::operator += (const Settings &other)
47 : {
48 0 : update(other);
49 :
50 0 : return *this;
51 : }
52 :
53 :
54 0 : Settings & Settings::operator = (const Settings &other)
55 : {
56 0 : if (&other == this)
57 0 : return *this;
58 :
59 0 : JMutexAutoLock lock(m_mutex);
60 0 : JMutexAutoLock lock2(other.m_mutex);
61 :
62 0 : clearNoLock();
63 0 : updateNoLock(other);
64 :
65 0 : return *this;
66 : }
67 :
68 :
69 247 : bool Settings::checkNameValid(const std::string &name)
70 : {
71 247 : bool valid = name.find_first_of("=\"{}#") == std::string::npos;
72 247 : if (valid) valid = trim(name) == name;
73 247 : if (!valid) {
74 0 : errorstream << "Invalid setting name \"" << name << "\""
75 0 : << std::endl;
76 0 : return false;
77 : }
78 247 : return true;
79 : }
80 :
81 :
82 247 : bool Settings::checkValueValid(const std::string &value)
83 : {
84 494 : if (value.substr(0, 3) == "\"\"\"" ||
85 247 : value.find("\n\"\"\"") != std::string::npos) {
86 : errorstream << "Invalid character sequence '\"\"\"' found in"
87 0 : " setting value!" << std::endl;
88 0 : return false;
89 : }
90 247 : return true;
91 : }
92 :
93 :
94 0 : std::string Settings::sanitizeName(const std::string &name)
95 : {
96 0 : std::string n = trim(name);
97 :
98 0 : for (const char *s = "=\"{}#"; *s; s++)
99 0 : n.erase(std::remove(n.begin(), n.end(), *s), n.end());
100 :
101 0 : return n;
102 : }
103 :
104 :
105 0 : std::string Settings::sanitizeValue(const std::string &value)
106 : {
107 0 : std::string v(value);
108 0 : size_t p = 0;
109 :
110 0 : if (v.substr(0, 3) == "\"\"\"")
111 0 : v.erase(0, 3);
112 :
113 0 : while ((p = v.find("\n\"\"\"")) != std::string::npos)
114 0 : v.erase(p, 4);
115 :
116 0 : return v;
117 : }
118 :
119 :
120 0 : std::string Settings::getMultiline(std::istream &is, size_t *num_lines)
121 : {
122 0 : size_t lines = 1;
123 0 : std::string value;
124 0 : std::string line;
125 :
126 0 : while (is.good()) {
127 0 : lines++;
128 0 : std::getline(is, line);
129 0 : if (line == "\"\"\"")
130 0 : break;
131 0 : value += line;
132 0 : value.push_back('\n');
133 : }
134 :
135 0 : size_t len = value.size();
136 0 : if (len)
137 0 : value.erase(len - 1);
138 :
139 0 : if (num_lines)
140 0 : *num_lines = lines;
141 :
142 0 : return value;
143 : }
144 :
145 :
146 12 : bool Settings::readConfigFile(const char *filename)
147 : {
148 24 : std::ifstream is(filename);
149 12 : if (!is.good())
150 0 : return false;
151 :
152 12 : return parseConfigLines(is, "");
153 : }
154 :
155 :
156 12 : bool Settings::parseConfigLines(std::istream &is, const std::string &end)
157 : {
158 24 : JMutexAutoLock lock(m_mutex);
159 :
160 24 : std::string line, name, value;
161 :
162 598 : while (is.good()) {
163 293 : std::getline(is, line);
164 293 : SettingsParseEvent event = parseConfigObject(line, end, name, value);
165 :
166 293 : switch (event) {
167 : case SPE_NONE:
168 : case SPE_INVALID:
169 : case SPE_COMMENT:
170 14 : break;
171 : case SPE_KVPAIR:
172 279 : m_settings[name] = SettingsEntry(value);
173 279 : break;
174 : case SPE_END:
175 0 : return true;
176 : case SPE_GROUP: {
177 0 : Settings *group = new Settings;
178 0 : if (!group->parseConfigLines(is, "}")) {
179 0 : delete group;
180 0 : return false;
181 : }
182 0 : m_settings[name] = SettingsEntry(group);
183 0 : break;
184 : }
185 : case SPE_MULTILINE:
186 0 : m_settings[name] = SettingsEntry(getMultiline(is));
187 0 : break;
188 : }
189 : }
190 :
191 12 : return end.empty();
192 : }
193 :
194 :
195 0 : void Settings::writeLines(std::ostream &os, u32 tab_depth) const
196 : {
197 0 : JMutexAutoLock lock(m_mutex);
198 :
199 0 : for (std::map<std::string, SettingsEntry>::const_iterator
200 0 : it = m_settings.begin();
201 0 : it != m_settings.end(); ++it)
202 0 : printEntry(os, it->first, it->second, tab_depth);
203 0 : }
204 :
205 :
206 0 : void Settings::printEntry(std::ostream &os, const std::string &name,
207 : const SettingsEntry &entry, u32 tab_depth)
208 : {
209 0 : for (u32 i = 0; i != tab_depth; i++)
210 0 : os << "\t";
211 :
212 0 : if (entry.is_group) {
213 0 : os << name << " = {\n";
214 :
215 0 : entry.group->writeLines(os, tab_depth + 1);
216 :
217 0 : for (u32 i = 0; i != tab_depth; i++)
218 0 : os << "\t";
219 0 : os << "}\n";
220 : } else {
221 0 : os << name << " = ";
222 :
223 0 : if (entry.value.find('\n') != std::string::npos)
224 0 : os << "\"\"\"\n" << entry.value << "\n\"\"\"\n";
225 : else
226 0 : os << entry.value << "\n";
227 : }
228 0 : }
229 :
230 :
231 1 : bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
232 : const std::string &end, u32 tab_depth)
233 : {
234 1 : std::map<std::string, SettingsEntry>::const_iterator it;
235 2 : std::set<std::string> present_entries;
236 2 : std::string line, name, value;
237 1 : bool was_modified = false;
238 1 : bool end_found = false;
239 :
240 : // Add any settings that exist in the config file with the current value
241 : // in the object if existing
242 107 : while (is.good() && !end_found) {
243 53 : std::getline(is, line);
244 53 : SettingsParseEvent event = parseConfigObject(line, end, name, value);
245 :
246 53 : switch (event) {
247 : case SPE_END:
248 0 : os << line << (is.eof() ? "" : "\n");
249 0 : end_found = true;
250 0 : break;
251 : case SPE_MULTILINE:
252 0 : value = getMultiline(is);
253 : /* FALLTHROUGH */
254 : case SPE_KVPAIR:
255 52 : it = m_settings.find(name);
256 104 : if (it != m_settings.end() &&
257 104 : (it->second.is_group || it->second.value != value)) {
258 0 : printEntry(os, name, it->second, tab_depth);
259 0 : was_modified = true;
260 : } else {
261 52 : os << line << "\n";
262 52 : if (event == SPE_MULTILINE)
263 0 : os << value << "\n\"\"\"\n";
264 : }
265 52 : present_entries.insert(name);
266 52 : break;
267 : case SPE_GROUP:
268 0 : it = m_settings.find(name);
269 0 : if (it != m_settings.end() && it->second.is_group) {
270 0 : os << line << "\n";
271 0 : sanity_check(it->second.group != NULL);
272 0 : was_modified |= it->second.group->updateConfigObject(is, os,
273 0 : "}", tab_depth + 1);
274 : } else {
275 0 : printEntry(os, name, it->second, tab_depth);
276 0 : was_modified = true;
277 : }
278 0 : present_entries.insert(name);
279 0 : break;
280 : default:
281 1 : os << line << (is.eof() ? "" : "\n");
282 1 : break;
283 : }
284 : }
285 :
286 : // Add any settings in the object that don't exist in the config file yet
287 53 : for (it = m_settings.begin(); it != m_settings.end(); ++it) {
288 52 : if (present_entries.find(it->first) != present_entries.end())
289 52 : continue;
290 :
291 0 : printEntry(os, it->first, it->second, tab_depth);
292 0 : was_modified = true;
293 : }
294 :
295 2 : return was_modified;
296 : }
297 :
298 :
299 1 : bool Settings::updateConfigFile(const char *filename)
300 : {
301 2 : JMutexAutoLock lock(m_mutex);
302 :
303 2 : std::ifstream is(filename);
304 2 : std::ostringstream os(std::ios_base::binary);
305 :
306 1 : bool was_modified = updateConfigObject(is, os, "");
307 1 : is.close();
308 :
309 1 : if (!was_modified)
310 1 : return true;
311 :
312 0 : if (!fs::safeWriteToFile(filename, os.str())) {
313 0 : errorstream << "Error writing configuration file: \""
314 0 : << filename << "\"" << std::endl;
315 0 : return false;
316 : }
317 :
318 0 : return true;
319 : }
320 :
321 :
322 1 : bool Settings::parseCommandLine(int argc, char *argv[],
323 : std::map<std::string, ValueSpec> &allowed_options)
324 : {
325 1 : int nonopt_index = 0;
326 1 : for (int i = 1; i < argc; i++) {
327 0 : std::string arg_name = argv[i];
328 0 : if (arg_name.substr(0, 2) != "--") {
329 : // If option doesn't start with -, read it in as nonoptX
330 0 : if (arg_name[0] != '-'){
331 0 : std::string name = "nonopt";
332 0 : name += itos(nonopt_index);
333 0 : set(name, arg_name);
334 0 : nonopt_index++;
335 0 : continue;
336 : }
337 0 : errorstream << "Invalid command-line parameter \""
338 0 : << arg_name << "\": --<option> expected." << std::endl;
339 0 : return false;
340 : }
341 :
342 0 : std::string name = arg_name.substr(2);
343 :
344 0 : std::map<std::string, ValueSpec>::iterator n;
345 0 : n = allowed_options.find(name);
346 0 : if (n == allowed_options.end()) {
347 0 : errorstream << "Unknown command-line parameter \""
348 0 : << arg_name << "\"" << std::endl;
349 0 : return false;
350 : }
351 :
352 0 : ValueType type = n->second.type;
353 :
354 0 : std::string value = "";
355 :
356 0 : if (type == VALUETYPE_FLAG) {
357 0 : value = "true";
358 : } else {
359 0 : if ((i + 1) >= argc) {
360 0 : errorstream << "Invalid command-line parameter \""
361 0 : << name << "\": missing value" << std::endl;
362 0 : return false;
363 : }
364 0 : value = argv[++i];
365 : }
366 :
367 0 : set(name, value);
368 : }
369 :
370 1 : return true;
371 : }
372 :
373 :
374 :
375 : /***********
376 : * Getters *
377 : ***********/
378 :
379 :
380 101446 : const SettingsEntry &Settings::getEntry(const std::string &name) const
381 : {
382 202892 : JMutexAutoLock lock(m_mutex);
383 :
384 101446 : std::map<std::string, SettingsEntry>::const_iterator n;
385 101446 : if ((n = m_settings.find(name)) == m_settings.end()) {
386 61660 : if ((n = m_defaults.find(name)) == m_defaults.end())
387 33 : throw SettingNotFoundException("Setting [" + name + "] not found.");
388 : }
389 202826 : return n->second;
390 : }
391 :
392 :
393 0 : Settings *Settings::getGroup(const std::string &name) const
394 : {
395 0 : const SettingsEntry &entry = getEntry(name);
396 0 : if (!entry.is_group)
397 0 : throw SettingNotFoundException("Setting [" + name + "] is not a group.");
398 0 : return entry.group;
399 : }
400 :
401 :
402 101446 : std::string Settings::get(const std::string &name) const
403 : {
404 101446 : const SettingsEntry &entry = getEntry(name);
405 101413 : if (entry.is_group)
406 0 : throw SettingNotFoundException("Setting [" + name + "] is a group.");
407 101413 : return entry.value;
408 : }
409 :
410 :
411 77020 : bool Settings::getBool(const std::string &name) const
412 : {
413 77020 : return is_yes(get(name));
414 : }
415 :
416 :
417 10171 : u16 Settings::getU16(const std::string &name) const
418 : {
419 10171 : return stoi(get(name), 0, 65535);
420 : }
421 :
422 :
423 4 : s16 Settings::getS16(const std::string &name) const
424 : {
425 4 : return stoi(get(name), -32768, 32767);
426 : }
427 :
428 :
429 1803 : s32 Settings::getS32(const std::string &name) const
430 : {
431 1803 : return stoi(get(name));
432 : }
433 :
434 :
435 7731 : float Settings::getFloat(const std::string &name) const
436 : {
437 7731 : return stof(get(name));
438 : }
439 :
440 :
441 0 : u64 Settings::getU64(const std::string &name) const
442 : {
443 0 : u64 value = 0;
444 0 : std::string s = get(name);
445 0 : std::istringstream ss(s);
446 0 : ss >> value;
447 0 : return value;
448 : }
449 :
450 :
451 0 : v2f Settings::getV2F(const std::string &name) const
452 : {
453 0 : v2f value;
454 0 : Strfnd f(get(name));
455 0 : f.next("(");
456 0 : value.X = stof(f.next(","));
457 0 : value.Y = stof(f.next(")"));
458 0 : return value;
459 : }
460 :
461 :
462 3 : v3f Settings::getV3F(const std::string &name) const
463 : {
464 3 : v3f value;
465 6 : Strfnd f(get(name));
466 3 : f.next("(");
467 3 : value.X = stof(f.next(","));
468 3 : value.Y = stof(f.next(","));
469 3 : value.Z = stof(f.next(")"));
470 6 : return value;
471 : }
472 :
473 :
474 0 : u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
475 : u32 *flagmask) const
476 : {
477 0 : std::string val = get(name);
478 0 : return std::isdigit(val[0])
479 0 : ? stoi(val)
480 0 : : readFlagString(val, flagdesc, flagmask);
481 : }
482 :
483 :
484 : // N.B. if getStruct() is used to read a non-POD aggregate type,
485 : // the behavior is undefined.
486 0 : bool Settings::getStruct(const std::string &name, const std::string &format,
487 : void *out, size_t olen) const
488 : {
489 0 : std::string valstr;
490 :
491 : try {
492 0 : valstr = get(name);
493 0 : } catch (SettingNotFoundException &e) {
494 0 : return false;
495 : }
496 :
497 0 : if (!deSerializeStringToStruct(valstr, format, out, olen))
498 0 : return false;
499 :
500 0 : return true;
501 : }
502 :
503 :
504 0 : bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
505 : {
506 0 : return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np);
507 : }
508 :
509 :
510 0 : bool Settings::getNoiseParamsFromValue(const std::string &name,
511 : NoiseParams &np) const
512 : {
513 0 : std::string value;
514 :
515 0 : if (!getNoEx(name, value))
516 0 : return false;
517 :
518 0 : Strfnd f(value);
519 :
520 0 : np.offset = stof(f.next(","));
521 0 : np.scale = stof(f.next(","));
522 0 : f.next("(");
523 0 : np.spread.X = stof(f.next(","));
524 0 : np.spread.Y = stof(f.next(","));
525 0 : np.spread.Z = stof(f.next(")"));
526 0 : f.next(",");
527 0 : np.seed = stoi(f.next(","));
528 0 : np.octaves = stoi(f.next(","));
529 0 : np.persist = stof(f.next(","));
530 :
531 0 : std::string optional_params = f.next("");
532 0 : if (optional_params != "")
533 0 : np.lacunarity = stof(optional_params);
534 :
535 0 : return true;
536 : }
537 :
538 :
539 0 : bool Settings::getNoiseParamsFromGroup(const std::string &name,
540 : NoiseParams &np) const
541 : {
542 0 : Settings *group = NULL;
543 :
544 0 : if (!getGroupNoEx(name, group))
545 0 : return false;
546 :
547 0 : group->getFloatNoEx("offset", np.offset);
548 0 : group->getFloatNoEx("scale", np.scale);
549 0 : group->getV3FNoEx("spread", np.spread);
550 0 : group->getS32NoEx("seed", np.seed);
551 0 : group->getU16NoEx("octaves", np.octaves);
552 0 : group->getFloatNoEx("persistence", np.persist);
553 0 : group->getFloatNoEx("lacunarity", np.lacunarity);
554 :
555 0 : np.flags = 0;
556 0 : if (!group->getFlagStrNoEx("flags", np.flags, flagdesc_noiseparams))
557 0 : np.flags = NOISE_FLAG_DEFAULTS;
558 :
559 0 : return true;
560 : }
561 :
562 :
563 23 : bool Settings::exists(const std::string &name) const
564 : {
565 46 : JMutexAutoLock lock(m_mutex);
566 :
567 111 : return (m_settings.find(name) != m_settings.end() ||
568 88 : m_defaults.find(name) != m_defaults.end());
569 : }
570 :
571 :
572 0 : std::vector<std::string> Settings::getNames() const
573 : {
574 0 : std::vector<std::string> names;
575 0 : for (std::map<std::string, SettingsEntry>::const_iterator
576 0 : i = m_settings.begin();
577 0 : i != m_settings.end(); ++i) {
578 0 : names.push_back(i->first);
579 : }
580 0 : return names;
581 : }
582 :
583 :
584 :
585 : /***************************************
586 : * Getters that don't throw exceptions *
587 : ***************************************/
588 :
589 0 : bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const
590 : {
591 : try {
592 0 : val = getEntry(name);
593 0 : return true;
594 0 : } catch (SettingNotFoundException &e) {
595 0 : return false;
596 : }
597 : }
598 :
599 :
600 0 : bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const
601 : {
602 : try {
603 0 : val = getGroup(name);
604 0 : return true;
605 0 : } catch (SettingNotFoundException &e) {
606 0 : return false;
607 : }
608 : }
609 :
610 :
611 0 : bool Settings::getNoEx(const std::string &name, std::string &val) const
612 : {
613 : try {
614 0 : val = get(name);
615 0 : return true;
616 0 : } catch (SettingNotFoundException &e) {
617 0 : return false;
618 : }
619 : }
620 :
621 :
622 16 : bool Settings::getFlag(const std::string &name) const
623 : {
624 : try {
625 16 : return getBool(name);
626 32 : } catch(SettingNotFoundException &e) {
627 16 : return false;
628 : }
629 : }
630 :
631 :
632 0 : bool Settings::getFloatNoEx(const std::string &name, float &val) const
633 : {
634 : try {
635 0 : val = getFloat(name);
636 0 : return true;
637 0 : } catch (SettingNotFoundException &e) {
638 0 : return false;
639 : }
640 : }
641 :
642 :
643 0 : bool Settings::getU16NoEx(const std::string &name, u16 &val) const
644 : {
645 : try {
646 0 : val = getU16(name);
647 0 : return true;
648 0 : } catch (SettingNotFoundException &e) {
649 0 : return false;
650 : }
651 : }
652 :
653 :
654 0 : bool Settings::getS16NoEx(const std::string &name, s16 &val) const
655 : {
656 : try {
657 0 : val = getS16(name);
658 0 : return true;
659 0 : } catch (SettingNotFoundException &e) {
660 0 : return false;
661 : }
662 : }
663 :
664 :
665 0 : bool Settings::getS32NoEx(const std::string &name, s32 &val) const
666 : {
667 : try {
668 0 : val = getS32(name);
669 0 : return true;
670 0 : } catch (SettingNotFoundException &e) {
671 0 : return false;
672 : }
673 : }
674 :
675 :
676 0 : bool Settings::getU64NoEx(const std::string &name, u64 &val) const
677 : {
678 : try {
679 0 : val = getU64(name);
680 0 : return true;
681 0 : } catch (SettingNotFoundException &e) {
682 0 : return false;
683 : }
684 : }
685 :
686 :
687 0 : bool Settings::getV2FNoEx(const std::string &name, v2f &val) const
688 : {
689 : try {
690 0 : val = getV2F(name);
691 0 : return true;
692 0 : } catch (SettingNotFoundException &e) {
693 0 : return false;
694 : }
695 : }
696 :
697 :
698 0 : bool Settings::getV3FNoEx(const std::string &name, v3f &val) const
699 : {
700 : try {
701 0 : val = getV3F(name);
702 0 : return true;
703 0 : } catch (SettingNotFoundException &e) {
704 0 : return false;
705 : }
706 : }
707 :
708 :
709 : // N.B. getFlagStrNoEx() does not set val, but merely modifies it. Thus,
710 : // val must be initialized before using getFlagStrNoEx(). The intention of
711 : // this is to simplify modifying a flags field from a default value.
712 0 : bool Settings::getFlagStrNoEx(const std::string &name, u32 &val,
713 : FlagDesc *flagdesc) const
714 : {
715 : try {
716 : u32 flags, flagmask;
717 :
718 0 : flags = getFlagStr(name, flagdesc, &flagmask);
719 :
720 0 : val &= ~flagmask;
721 0 : val |= flags;
722 :
723 0 : return true;
724 0 : } catch (SettingNotFoundException &e) {
725 0 : return false;
726 : }
727 : }
728 :
729 :
730 : /***********
731 : * Setters *
732 : ***********/
733 :
734 247 : bool Settings::setEntry(const std::string &name, const void *data,
735 : bool set_group, bool set_default)
736 : {
737 247 : Settings *old_group = NULL;
738 :
739 247 : if (!checkNameValid(name))
740 0 : return false;
741 247 : if (!set_group && !checkValueValid(*(const std::string *)data))
742 0 : return false;
743 :
744 : {
745 494 : JMutexAutoLock lock(m_mutex);
746 :
747 247 : SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name];
748 247 : old_group = entry.group;
749 :
750 247 : entry.value = set_group ? "" : *(const std::string *)data;
751 247 : entry.group = set_group ? *(Settings **)data : NULL;
752 247 : entry.is_group = set_group;
753 : }
754 :
755 247 : delete old_group;
756 :
757 247 : return true;
758 : }
759 :
760 :
761 12 : bool Settings::set(const std::string &name, const std::string &value)
762 : {
763 12 : if (!setEntry(name, &value, false, false))
764 0 : return false;
765 :
766 12 : doCallbacks(name);
767 12 : return true;
768 : }
769 :
770 :
771 235 : bool Settings::setDefault(const std::string &name, const std::string &value)
772 : {
773 235 : return setEntry(name, &value, false, true);
774 : }
775 :
776 :
777 0 : bool Settings::setGroup(const std::string &name, Settings *group)
778 : {
779 0 : return setEntry(name, &group, true, false);
780 : }
781 :
782 :
783 0 : bool Settings::setGroupDefault(const std::string &name, Settings *group)
784 : {
785 0 : return setEntry(name, &group, true, true);
786 : }
787 :
788 :
789 0 : bool Settings::setBool(const std::string &name, bool value)
790 : {
791 0 : return set(name, value ? "true" : "false");
792 : }
793 :
794 :
795 0 : bool Settings::setS16(const std::string &name, s16 value)
796 : {
797 0 : return set(name, itos(value));
798 : }
799 :
800 :
801 0 : bool Settings::setU16(const std::string &name, u16 value)
802 : {
803 0 : return set(name, itos(value));
804 : }
805 :
806 :
807 0 : bool Settings::setS32(const std::string &name, s32 value)
808 : {
809 0 : return set(name, itos(value));
810 : }
811 :
812 :
813 0 : bool Settings::setU64(const std::string &name, u64 value)
814 : {
815 0 : std::ostringstream os;
816 0 : os << value;
817 0 : return set(name, os.str());
818 : }
819 :
820 :
821 0 : bool Settings::setFloat(const std::string &name, float value)
822 : {
823 0 : return set(name, ftos(value));
824 : }
825 :
826 :
827 0 : bool Settings::setV2F(const std::string &name, v2f value)
828 : {
829 0 : std::ostringstream os;
830 0 : os << "(" << value.X << "," << value.Y << ")";
831 0 : return set(name, os.str());
832 : }
833 :
834 :
835 0 : bool Settings::setV3F(const std::string &name, v3f value)
836 : {
837 0 : std::ostringstream os;
838 0 : os << "(" << value.X << "," << value.Y << "," << value.Z << ")";
839 0 : return set(name, os.str());
840 : }
841 :
842 :
843 0 : bool Settings::setFlagStr(const std::string &name, u32 flags,
844 : const FlagDesc *flagdesc, u32 flagmask)
845 : {
846 0 : return set(name, writeFlagString(flags, flagdesc, flagmask));
847 : }
848 :
849 :
850 0 : bool Settings::setStruct(const std::string &name, const std::string &format,
851 : void *value)
852 : {
853 0 : std::string structstr;
854 0 : if (!serializeStructToString(&structstr, format, value))
855 0 : return false;
856 :
857 0 : return set(name, structstr);
858 : }
859 :
860 :
861 0 : bool Settings::setNoiseParams(const std::string &name,
862 : const NoiseParams &np, bool set_default)
863 : {
864 0 : Settings *group = new Settings;
865 :
866 0 : group->setFloat("offset", np.offset);
867 0 : group->setFloat("scale", np.scale);
868 0 : group->setV3F("spread", np.spread);
869 0 : group->setS32("seed", np.seed);
870 0 : group->setU16("octaves", np.octaves);
871 0 : group->setFloat("persistence", np.persist);
872 0 : group->setFloat("lacunarity", np.lacunarity);
873 0 : group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags);
874 :
875 0 : return setEntry(name, &group, true, set_default);
876 : }
877 :
878 :
879 0 : bool Settings::remove(const std::string &name)
880 : {
881 0 : JMutexAutoLock lock(m_mutex);
882 :
883 0 : delete m_settings[name].group;
884 0 : return m_settings.erase(name);
885 : }
886 :
887 :
888 13 : void Settings::clear()
889 : {
890 26 : JMutexAutoLock lock(m_mutex);
891 13 : clearNoLock();
892 13 : }
893 :
894 0 : void Settings::clearDefaults()
895 : {
896 0 : JMutexAutoLock lock(m_mutex);
897 0 : clearDefaultsNoLock();
898 0 : }
899 :
900 0 : void Settings::updateValue(const Settings &other, const std::string &name)
901 : {
902 0 : if (&other == this)
903 0 : return;
904 :
905 0 : JMutexAutoLock lock(m_mutex);
906 :
907 : try {
908 0 : std::string val = other.get(name);
909 :
910 0 : m_settings[name] = val;
911 0 : } catch (SettingNotFoundException &e) {
912 : }
913 : }
914 :
915 :
916 0 : void Settings::update(const Settings &other)
917 : {
918 0 : if (&other == this)
919 0 : return;
920 :
921 0 : JMutexAutoLock lock(m_mutex);
922 0 : JMutexAutoLock lock2(other.m_mutex);
923 :
924 0 : updateNoLock(other);
925 : }
926 :
927 :
928 346 : SettingsParseEvent Settings::parseConfigObject(const std::string &line,
929 : const std::string &end, std::string &name, std::string &value)
930 : {
931 692 : std::string trimmed_line = trim(line);
932 :
933 346 : if (trimmed_line.empty())
934 15 : return SPE_NONE;
935 331 : if (trimmed_line[0] == '#')
936 0 : return SPE_COMMENT;
937 331 : if (trimmed_line == end)
938 0 : return SPE_END;
939 :
940 331 : size_t pos = trimmed_line.find('=');
941 331 : if (pos == std::string::npos)
942 0 : return SPE_INVALID;
943 :
944 331 : name = trim(trimmed_line.substr(0, pos));
945 331 : value = trim(trimmed_line.substr(pos + 1));
946 :
947 331 : if (value == "{")
948 0 : return SPE_GROUP;
949 331 : if (value == "\"\"\"")
950 0 : return SPE_MULTILINE;
951 :
952 331 : return SPE_KVPAIR;
953 : }
954 :
955 :
956 0 : void Settings::updateNoLock(const Settings &other)
957 : {
958 0 : m_settings.insert(other.m_settings.begin(), other.m_settings.end());
959 0 : m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
960 0 : }
961 :
962 :
963 13 : void Settings::clearNoLock()
964 : {
965 13 : std::map<std::string, SettingsEntry>::const_iterator it;
966 292 : for (it = m_settings.begin(); it != m_settings.end(); ++it)
967 279 : delete it->second.group;
968 13 : m_settings.clear();
969 :
970 13 : clearDefaultsNoLock();
971 13 : }
972 :
973 13 : void Settings::clearDefaultsNoLock()
974 : {
975 13 : std::map<std::string, SettingsEntry>::const_iterator it;
976 248 : for (it = m_defaults.begin(); it != m_defaults.end(); ++it)
977 235 : delete it->second.group;
978 13 : m_defaults.clear();
979 13 : }
980 :
981 :
982 9 : void Settings::registerChangedCallback(std::string name,
983 : setting_changed_callback cbf, void *userdata)
984 : {
985 18 : JMutexAutoLock lock(m_callbackMutex);
986 9 : m_callbacks[name].push_back(std::make_pair(cbf, userdata));
987 9 : }
988 :
989 1 : void Settings::deregisterChangedCallback(std::string name, setting_changed_callback cbf, void *userdata)
990 : {
991 2 : JMutexAutoLock lock(m_callbackMutex);
992 1 : std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
993 1 : if (iterToVector != m_callbacks.end())
994 : {
995 1 : std::vector<std::pair<setting_changed_callback, void*> > &vector = iterToVector->second;
996 :
997 : std::vector<std::pair<setting_changed_callback, void*> >::iterator position =
998 1 : std::find(vector.begin(), vector.end(), std::make_pair(cbf, userdata));
999 :
1000 1 : if (position != vector.end())
1001 1 : vector.erase(position);
1002 : }
1003 1 : }
1004 :
1005 12 : void Settings::doCallbacks(const std::string name)
1006 : {
1007 24 : JMutexAutoLock lock(m_callbackMutex);
1008 12 : std::map<std::string, std::vector<std::pair<setting_changed_callback, void*> > >::iterator iterToVector = m_callbacks.find(name);
1009 12 : if (iterToVector != m_callbacks.end())
1010 : {
1011 0 : std::vector<std::pair<setting_changed_callback, void*> >::iterator iter;
1012 0 : for (iter = iterToVector->second.begin(); iter != iterToVector->second.end(); iter++)
1013 : {
1014 0 : (iter->first)(name, iter->second);
1015 : }
1016 : }
1017 15 : }
|