Mana
Loading...
Searching...
No Matches
configuration.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2024 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "configuration.h"
23
24#include "event.h"
25#include "log.h"
26
27#include "utils/stringutils.h"
28#include "utils/xml.h"
29
30void ConfigurationObject::setValue(const std::string &key,
31 const std::string &value)
32{
33 mOptions[key] = value;
34}
35
36std::string ConfigurationObject::getValue(const std::string &key,
37 const std::string &deflt) const
38{
39 auto iter = mOptions.find(key);
40 return iter != mOptions.end() ? iter->second : deflt;
41}
42
43int ConfigurationObject::getValue(const std::string &key, int deflt) const
44{
45 auto iter = mOptions.find(key);
46 return iter != mOptions.end() ? atoi(iter->second.c_str()) : deflt;
47}
48
49unsigned ConfigurationObject::getValue(const std::string &key,
50 unsigned deflt) const
51{
52 auto iter = mOptions.find(key);
53 return iter != mOptions.end() ? atol(iter->second.c_str()) : deflt;
54}
55
56double ConfigurationObject::getValue(const std::string &key,
57 double deflt) const
58{
59 auto iter = mOptions.find(key);
60 return iter != mOptions.end() ? atof(iter->second.c_str()) : deflt;
61}
62
64{
65 mOptions.clear();
66}
67
72
74{
75 if (mDefaultsData)
76 {
77 for (auto &[_, variableData] : *mDefaultsData)
78 {
79 delete variableData;
80 }
81 delete mDefaultsData;
82 mDefaultsData = nullptr;
83 }
84}
85
90
92{
94 mDefaultsData = defaultsData;
95}
96
98 VariableData::DataType type) const
99{
100 if (mDefaultsData)
101 {
102 auto itdef = mDefaultsData->find(key);
103
104 if (itdef != mDefaultsData->end() && itdef->second
105 && itdef->second->getType() == type)
106 {
107 return itdef->second;
108 }
109
110 Log::info("%s: No value in registry for key %s",
111 mConfigPath.c_str(),
112 key.c_str());
113 }
114
115 return nullptr;
116}
117
118int Configuration::getIntValue(const std::string &key) const
119{
120 int defaultValue = 0;
121 auto iter = mOptions.find(key);
122 if (iter == mOptions.end())
123 {
125 if (vd)
126 defaultValue = ((IntData*)vd)->getData();
127 }
128 else
129 {
130 defaultValue = atoi(iter->second.c_str());
131 }
132
133 return defaultValue;
134}
135
136std::string Configuration::getStringValue(const std::string &key) const
137{
138 std::string defaultValue;
139 auto iter = mOptions.find(key);
140 if (iter == mOptions.end())
141 {
143 defaultValue = ((StringData*)vd)->getData();
144 }
145 else
146 {
147 defaultValue = iter->second;
148 }
149
150 return defaultValue;
151}
152
153
154float Configuration::getFloatValue(const std::string &key) const
155{
156 float defaultValue = 0.0f;
157 auto iter = mOptions.find(key);
158 if (iter == mOptions.end())
159 {
161 defaultValue = ((FloatData*)vd)->getData();
162 }
163 else
164 {
165 defaultValue = atof(iter->second.c_str());
166 }
167
168 return defaultValue;
169}
170
171bool Configuration::getBoolValue(const std::string &key) const
172{
173 bool defaultValue = false;
174 auto iter = mOptions.find(key);
175 if (iter == mOptions.end())
176 {
178 defaultValue = ((BoolData*)vd)->getData();
179 }
180 else
181 {
182 return getBoolFromString(iter->second, defaultValue);
183 }
184
185 return defaultValue;
186}
187
189{
190 clear();
191
192 for (auto node : parent_node.children())
193 {
194 if (node.name() == "option")
195 {
196 std::string name = node.getProperty("name", std::string());
197
198 if (!name.empty())
199 mOptions[name] = node.getProperty("value", std::string());
200 }
201 }
202}
203
204void Configuration::init(const std::string &filename, bool useResManager)
205{
206 XML::Document doc(filename, useResManager);
207
208 if (useResManager)
209 mConfigPath = "PhysFS://" + filename;
210 else
211 mConfigPath = filename;
212
213 XML::Node rootNode = doc.rootNode();
214
215 if (!rootNode)
216 {
217 Log::info("Couldn't open configuration file: %s", filename.c_str());
218 return;
219 }
220
221 if (rootNode.name() != "configuration")
222 {
223 Log::warn("No configuration file (%s)", filename.c_str());
224 return;
225 }
226
227 initFromXML(rootNode);
228}
229
230
231template<typename T>
232struct Option
233{
234 Option(const char *name, const T &value, const T &defaultValue)
236 {}
237
238 const char *name;
239 const T &value;
240 const T &defaultValue;
241};
242
243template<typename T>
244static void serialize(XML::Writer &writer, const Option<T> &option)
245{
246 if (option.value == option.defaultValue)
247 return;
248
249 writer.startElement("option");
250 writer.addAttribute("name", option.name);
251 writer.addAttribute("value", option.value);
252 writer.endElement();
253}
254
255static void serialize(XML::Writer &writer, const ItemShortcutEntry &itemShortcut)
256{
257 writer.startElement("itemshortcut");
258 writer.addAttribute("index", itemShortcut.index);
259 writer.addAttribute("id", itemShortcut.itemId);
260 writer.endElement();
261}
262
263static void serialize(XML::Writer &writer, const EmoteShortcutEntry &emoteShortcut)
264{
265 writer.startElement("emoteshortcut");
266 writer.addAttribute("index", emoteShortcut.index);
267 writer.addAttribute("id", emoteShortcut.emoteId);
268 writer.endElement();
269}
270
271static void serialize(XML::Writer &writer, const Outfit &outfit)
272{
273 writer.startElement("outfit");
274 writer.addAttribute("index", outfit.index);
275 writer.addAttribute("items", outfit.items);
276 writer.addAttribute("unequip", outfit.unequip);
277 writer.endElement();
278}
279
280static void serialize(XML::Writer &writer, const UserColor &color)
281{
282 if (!color.color.empty())
283 writer.addAttribute("color", color.color);
284
285 writer.addAttribute("gradient", color.gradient);
286
287 if (color.delay)
288 writer.addAttribute("delay", *color.delay);
289}
290
291static void serialize(XML::Writer &writer, const WindowState &state)
292{
293 if (state.x) writer.addAttribute("x", *state.x);
294 if (state.y) writer.addAttribute("y", *state.y);
295 if (state.width) writer.addAttribute("width", *state.width);
296 if (state.height) writer.addAttribute("height", *state.height);
297 if (state.visible) writer.addAttribute("visible", *state.visible);
298 if (state.sticky) writer.addAttribute("sticky", *state.sticky);
299}
300
301static const char *serverTypeToString(ServerType type)
302{
303 switch (type)
304 {
306 return "tmwathena";
308 return "manaserv";
309 default:
310 return "";
311 }
312}
313
314static void serialize(XML::Writer &writer, const ServerInfo &server)
315{
316 writer.startElement("server");
317
318 writer.addAttribute("name", server.name);
319 writer.addAttribute("type", serverTypeToString(server.type));
320
321 writer.startElement("connection");
322 writer.addAttribute("hostname", server.hostname);
323 writer.addAttribute("port", server.port);
324 writer.endElement(); // connection
325
326 if (!server.description.empty())
327 {
328 writer.startElement("description");
329 writer.writeText(server.description);
330 writer.endElement();
331 }
332
333 if (!server.persistentIp)
334 {
335 writer.startElement("persistentIp");
336 writer.writeText(server.persistentIp ? "1" : "0");
337 writer.endElement();
338 }
339
340 writer.endElement(); // server
341}
342
343template<typename T>
344void serdeOptions(T option)
345{
346 option("OverlayDetail", &Config::overlayDetail);
347 option("speechBubblecolor", &Config::speechBubblecolor);
348 option("speechBubbleAlpha", &Config::speechBubbleAlpha);
349 option("speech", &Config::speech);
350 option("visiblenames", &Config::visibleNames);
351 option("showgender", &Config::showGender);
352 option("showMonstersTakedDamage", &Config::showMonstersTakedDamage);
353 option("showWarps", &Config::showWarps);
354 option("hideCompletedQuests", &Config::hideCompletedQuests);
355 option("particleMaxCount", &Config::particleMaxCount);
356 option("particleFastPhysics", &Config::particleFastPhysics);
357 option("particleEmitterSkip", &Config::particleEmitterSkip);
358 option("particleeffects", &Config::particleEffects);
359 option("logToStandardOut", &Config::logToStandardOut);
360 option("opengl", &Config::opengl);
361 option("vsync", &Config::vsync);
362 option("windowmode", &Config::windowMode);
363 option("screenwidth", &Config::screenWidth);
364 option("screenheight", &Config::screenHeight);
365 option("scale", &Config::scale);
366 option("sound", &Config::sound);
367 option("sfxVolume", &Config::sfxVolume);
368 option("notificationsVolume", &Config::notificationsVolume);
369 option("musicVolume", &Config::musicVolume);
370 option("fpslimit", &Config::fpsLimit);
371
372 option("remember", &Config::remember);
373 option("username", &Config::username);
374 option("lastCharacter", &Config::lastCharacter);
375 option("updatehost", &Config::updatehost);
376 option("screenshotDirectory", &Config::screenshotDirectory);
377 option("screenshotDirectorySuffix", &Config::screenshotDirectorySuffix);
378 option("useScreenshotDirectorySuffix", &Config::useScreenshotDirectorySuffix);
379
380 option("EnableSync", &Config::enableSync);
381
382 option("joystickEnabled", &Config::joystickEnabled);
383 option("upTolerance", &Config::upTolerance);
384 option("downTolerance", &Config::downTolerance);
385 option("leftTolerance", &Config::leftTolerance);
386 option("rightTolerance", &Config::rightTolerance);
387
388 option("logNpcInGui", &Config::logNpcInGui);
389 option("download-music", &Config::downloadMusic);
390 option("guialpha", &Config::guiAlpha);
391 option("ChatLogLength", &Config::chatLogLength);
392 option("enableChatLog", &Config::enableChatLog);
393 option("whispertab", &Config::whisperTab);
394 option("customcursor", &Config::customCursor);
395 option("showownname", &Config::showOwnName);
396 option("showpickupparticle", &Config::showPickupParticle);
397 option("showpickupchat", &Config::showPickupChat);
398 option("showMinimap", &Config::showMinimap);
399 option("fontSize", &Config::fontSize);
400 option("ReturnToggles", &Config::returnTogglesChat);
401 option("ScrollLaziness", &Config::scrollLaziness);
402 option("ScrollRadius", &Config::scrollRadius);
403 option("ScrollCenterOffsetX", &Config::scrollCenterOffsetX);
404 option("ScrollCenterOffsetY", &Config::scrollCenterOffsetY);
405 option("onlineServerList", &Config::onlineServerList);
406 option("theme", &Config::theme);
407 option("disableTransparency", &Config::disableTransparency);
408
409 option("persistent-player-list", &Config::persistentPlayerList);
410 option("player-ignore-strategy", &Config::playerIgnoreStrategy);
411 option("default-player-permissions", &Config::defaultPlayerPermissions);
412}
413
414void serialize(XML::Writer &writer, const Config &config)
415{
416 const Config defaults;
417 auto serializeOption = [&](const char *name, auto member) {
418 serialize(writer, Option { name, config.*member, defaults.*member });
419 };
420
421 writer.startElement("configuration");
422
423 serdeOptions(serializeOption);
424
425 for (const auto &[name, value] : config.unknownOptions)
426 serialize(writer, Option { name.c_str(), value, std::string() });
427
428 for (const auto &[action, key] : config.keys)
429 {
430 writer.startElement("key");
431 writer.addAttribute("action", action);
432 writer.addAttribute("key", key);
433 writer.endElement();
434 }
435
436 for (auto &itemShortcut : config.itemShortcuts)
437 serialize(writer, itemShortcut);
438
440 serialize(writer, emoteShortcut);
441
442 for (auto &outfit : config.outfits)
443 serialize(writer, outfit);
444
445 for (auto &[type, color] : config.colors)
446 {
447 writer.startElement("color");
448 writer.addAttribute("type", type);
449
450 serialize(writer, color);
451
452 writer.endElement();
453 }
454
455 for (const auto &[name, state] : config.windows)
456 {
457 writer.startElement("window");
458 writer.addAttribute("name", name);
459
460 serialize(writer, state);
461
462 writer.endElement(); // window
463 }
464
465 for (const auto &server : config.servers)
466 {
467 if (server.save && server.isValid())
468 serialize(writer, server);
469 }
470
471 for (const auto &[name, relation] : config.players)
472 {
473 writer.startElement("player");
474 writer.addAttribute("name", name);
475 writer.addAttribute("relation", static_cast<int>(relation));
476 writer.endElement();
477 }
478
479 writer.endElement(); // configuration
480}
481
483{
484 node.attribute("index", itemShortcut.index);
485 node.attribute("id", itemShortcut.itemId);
486}
487
489{
490 node.attribute("index", emoteShortcut.index);
491 node.attribute("id", emoteShortcut.emoteId);
492}
493
494void deserialize(XML::Node node, Outfit &outfit)
495{
496 node.attribute("index", outfit.index);
497 node.attribute("items", outfit.items);
498 node.attribute("unequip", outfit.unequip);
499}
500
502{
503 node.attribute("color", color.color);
504 node.attribute("gradient", color.gradient);
505 node.attribute("delay", color.delay);
506}
507
509{
510 node.attribute("x", state.x);
511 node.attribute("y", state.y);
512 node.attribute("width", state.width);
513 node.attribute("height", state.height);
514 node.attribute("visible", state.visible);
515 node.attribute("sticky", state.sticky);
516}
517
519{
520 node.attribute("name", server.name);
521
522 std::string type;
523 node.attribute("type", type);
524 server.type = ServerInfo::parseType(type);
525 server.save = true;
526
527 for (auto node : node.children()) {
528 if (node.name() == "connection") {
529 node.attribute("hostname", server.hostname);
530 node.attribute("port", server.port);
531 } else if (node.name() == "description") {
532 server.description = node.textContent();
533 } else if (node.name() == "persistentIp") {
534 const std::string value { node.textContent() };
535 server.persistentIp = getBoolFromString(value, server.persistentIp);
536 }
537 }
538}
539
541{
542 std::map<std::string, std::string> options;
543
544 for (auto node : node.children()) {
545 if (node.name() == "option") {
546 std::string name;
547 if (!node.attribute("name", name))
548 continue;
549 node.attribute("value", options[name]);
550 } else if (node.name() == "list") {
551 // Backwards compatibility for old configuration files
552 for (auto node : node.children()) {
553 if (node.name() == "player") {
554 std::string playerName;
556
557 for (auto node : node.children()) {
558 if (node.name() == "option") {
559 std::string optionName;
560
561 if (node.attribute("name", optionName)) {
562 if (optionName == "name")
563 node.attribute("value", playerName);
564 else if (optionName == "relation")
565 node.attribute("value", relation);
566 }
567 }
568 }
569
570 if (!playerName.empty())
571 config.players[playerName] = relation;
572 }
573 }
574 } else if (node.name() == "key") {
575 std::string action;
576 node.attribute("action", action);
577 if (!action.empty())
578 node.attribute("key", config.keys[action]);
579 } else if (node.name() == "itemshortcut") {
580 deserialize(node, config.itemShortcuts.emplace_back());
581 } else if (node.name() == "emoteshortcut") {
582 deserialize(node, config.emoteShortcuts.emplace_back());
583 } else if (node.name() == "outfit") {
584 deserialize(node, config.outfits.emplace_back());
585 } else if (node.name() == "color") {
586 std::string type;
587 node.attribute("type", type);
588 deserialize(node, config.colors[type]);
589 } else if (node.name() == "window") {
590 std::string name;
591 node.attribute("name", name);
592 deserialize(node, config.windows[name]);
593 } else if (node.name() == "player") {
594 std::string name;
595 node.attribute("name", name);
596 if (!name.empty())
597 node.attribute("relation", config.players[name]);
598 } else if (node.name() == "server") {
599 deserialize(node, config.servers.emplace_back());
600 }
601 }
602
603 auto deserializeOption = [&](const char *name, auto member) {
604 auto it = options.find(name);
605 if (it == options.end())
606 return;
607
608 fromString(it->second.data(), config.*member);
609 options.erase(it);
610 };
611
612 serdeOptions(deserializeOption);
613
614 config.unknownOptions = std::move(options);
615}
std::string getValue(const std::string &key, const std::string &deflt) const
Gets a value as string.
std::map< std::string, std::string > mOptions
void clear()
Re-sets all data in the configuration.
void initFromXML(XML::Node node)
virtual ~ConfigurationObject()
void setValue(const std::string &key, const std::string &value)
Sets an option using a string value.
bool getBoolValue(const std::string &key) const
std::string getStringValue(const std::string &key) const
VariableData * getDefault(const std::string &key, VariableData::DataType type) const
float getFloatValue(const std::string &key) const
DefaultsData * mDefaultsData
Defaults of value for a given key.
std::string mConfigPath
Location of config file.
void setDefaultValues(DefaultsData *defaultsData)
Set the default values for each keys.
~Configuration() override
void cleanDefaults()
Clean up the default values member.
int getIntValue(const std::string &key) const
returns a value corresponding to the given key.
void init(const std::string &filename, bool useResManager=false)
Reads config file and parse all options into memory.
std::string hostname
Definition serverinfo.h:42
static ServerType parseType(const std::string &type)
Definition serverinfo.h:73
uint16_t port
Definition serverinfo.h:43
bool isValid() const
Definition serverinfo.h:51
ServerType type
Definition serverinfo.h:40
bool persistentIp
Definition serverinfo.h:49
std::string name
Definition serverinfo.h:41
std::string description
Definition serverinfo.h:45
A helper class for parsing an XML document, which also cleans it up again (RAII).
Definition xml.h:190
Node rootNode() const
Returns the root node of the document (or NULL if there was a load error).
Definition xml.h:213
std::string_view name() const
Definition xml.h:46
Children children() const
Definition xml.h:97
std::string_view textContent() const
Definition xml.h:105
bool attribute(const char *name, T &value) const
Definition xml.h:129
Helper class for writing out XML data.
Definition xml.h:229
void writeText(const std::string &text)
Definition xml.h:301
void startElement(const char *name)
Definition xml.h:261
void addAttribute(const char *name, const std::string &value)
Definition xml.h:271
void endElement()
Definition xml.h:266
Config config
Global settings (config.xml)
Definition client.cpp:97
void deserialize(XML::Node node, ItemShortcutEntry &itemShortcut)
void serdeOptions(T option)
std::map< std::string, VariableData * > DefaultsData
Definition defaults.h:27
EmoteShortcut * emoteShortcut
#define _(s)
Definition gettext.h:38
ItemShortcut * itemShortcut
void warn(const char *log_text,...) LOG_PRINTF_ATTR
void info(const char *log_text,...) LOG_PRINTF_ATTR
PlayerRelation
ServerType
Definition serverinfo.h:29
bool getBoolFromString(std::string text, bool def)
Returns a bool value depending on the given string value.
float guiAlpha
bool showPickupChat
bool logToStandardOut
std::vector< Outfit > outfits
bool persistentPlayerList
int downTolerance
std::map< std::string, PlayerRelation > players
std::map< std::string, WindowState > windows
bool opengl
bool joystickEnabled
int screenWidth
bool customCursor
bool showMinimap
std::vector< ItemShortcutEntry > itemShortcuts
int overlayDetail
std::map< std::string, std::string > unknownOptions
bool returnTogglesChat
ServerInfos servers
bool visibleNames
int scrollCenterOffsetX
int scrollLaziness
std::map< std::string, UserColor > colors
bool enableChatLog
bool enableSync
std::string playerIgnoreStrategy
std::string lastCharacter
Being::Speech speech
bool whisperTab
bool particleEffects
std::string speechBubblecolor
bool logNpcInGui
std::string updatehost
bool showWarps
int particleFastPhysics
WindowMode windowMode
int screenHeight
int sfxVolume
int notificationsVolume
bool showOwnName
int scrollRadius
float speechBubbleAlpha
int chatLogLength
bool disableTransparency
std::map< std::string, std::string > keys
std::vector< EmoteShortcutEntry > emoteShortcuts
bool useScreenshotDirectorySuffix
bool showPickupParticle
int particleEmitterSkip
bool hideCompletedQuests
std::string onlineServerList
std::string theme
bool showGender
int leftTolerance
bool showMonstersTakedDamage
int particleMaxCount
int musicVolume
bool downloadMusic
unsigned defaultPlayerPermissions
int rightTolerance
std::string screenshotDirectory
std::string username
int upTolerance
std::string screenshotDirectorySuffix
bool remember
int scrollCenterOffsetY
const char * name
Option(const char *name, const T &value, const T &defaultValue)
const T & defaultValue
const T & value
bool unequip
std::string items
std::string color
std::optional< int > delay
std::optional< int > height
std::optional< bool > visible
std::optional< int > x
std::optional< int > y
std::optional< bool > sticky
std::optional< int > width
void fromString(const char *str, FillMode &value)
Definition theme.cpp:640